use crate::storage::associations::{BelongsTo, HasMany};
use crate::storage::config_store::ConfigStore;
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::sync::Arc;
use tonic::Status;
use url::Url;

pub async fn new<B: Clone + Debug + Display + Sync + Send + 'static>(
    url: &Url,
    app_name: &str,
    belongs_tos_by_bucket: HashMap<String, Vec<BelongsTo<B>>>,
    has_manys_by_bucket: HashMap<String, Vec<HasMany<B>>>,
) -> Result<Arc<dyn ConfigStore<B>>, Status> {
    Ok(match url.scheme() {
        "memory" => {
            let opts = super::memory::Options {
                app_name: Some(app_name.to_string()),
                belongs_tos_by_bucket,
                has_manys_by_bucket,
            };
            Arc::new(super::memory::MemoryConfigStore::new(opts))
        }
        #[cfg(feature = "storage-mongodb")]
        "mongodb" | "mongodb+srv" => {
            let index_models_by_bucket = Default::default();
            let opts = super::mongodb::Options {
                app_name: Some(app_name.to_string()),
                belongs_tos_by_bucket,
                has_manys_by_bucket,
                index_models_by_bucket,
            };
            Arc::new(
                super::mongodb::MongodbConfigStore::new(url, opts)
                    .await
                    .map_err(|e| Status::internal(e.to_string()))?,
            )
        }
        #[cfg(feature = "storage-postgres")]
        "postgres" => {
            use sqlx::postgres::PgPoolOptions;
            let pg_pool_options = PgPoolOptions::new().max_connections(10);
            let opts = super::postgres::Options {
                app_name: Some(app_name.to_string()),
                migrator: None,
                pg_pool_options,
            };
            Arc::new(
                super::postgres::PostgresConfigStore::new(url, opts)
                    .await
                    .map_err(|e| Status::internal(e.to_string()))?,
            )
        }
        _ => {
            return Err(Status::internal("A configured config store is required"));
        }
    })
}

#[cfg(test)]
mod tests {
    use crate::storage::config_store::ConfigStore;
    use std::collections::HashMap;
    use std::sync::Arc;

    const TEST_CONFIG_STORE_URL: &str = "TEST_CONFIG_STORE_URL";

    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
    async fn can_connect() {
        let config_store_url = match std::env::var(TEST_CONFIG_STORE_URL) {
            Ok(url) => url.parse().unwrap(),
            _ => {
                eprintln!("Skipping test because no {TEST_CONFIG_STORE_URL} is set");
                return;
            }
        };
        let belongs_tos_by_bucket = HashMap::new();
        let has_manys_by_bucket = HashMap::new();
        let _config_store: Arc<dyn ConfigStore<String>> = super::new(
            &config_store_url,
            "saas-rs-sdk-tests",
            belongs_tos_by_bucket,
            has_manys_by_bucket,
        )
        .await
        .unwrap();
    }
}
