use crate::storage::session_store::SessionStore;
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,
) -> Result<Arc<dyn SessionStore<B>>, Status> {
    let belongs_tos_by_bucket = HashMap::new();
    let has_manys_by_bucket = HashMap::new();
    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::MemorySessionStore::new(opts))
        }
        #[cfg(feature = "storage-redis")]
        "redis" | "redis-cluster" | "redis-sentinel" | "redis+unix" => {
            let opts = super::redis::Options {
                app_name: Some(app_name.to_string()),
                belongs_tos_by_bucket,
                has_manys_by_bucket,
            };
            Arc::new(super::redis::RedisSessionStore::new(url, opts).await?)
        }
        _ => {
            return Err(Status::internal("A configured config store is required"));
        }
    })
}

#[cfg(test)]
mod tests {
    use crate::storage::session_store::SessionStore;
    use std::sync::Arc;

    const TEST_SESSION_STORE_URL: &str = "TEST_SESSION_STORE_URL";

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