use std::env::{self, temp_dir};

use atuin_server_database::{DbSettings, DbType};
use snowflake_uid::{Config, Generator};
use sqlx::migrate::MigrateDatabase;
use url::Url;

fn get_settings(env_uri: Option<String>) -> eyre::Result<DbSettings> {
    let db_uri = env_uri.unwrap_or_else(|| {
        let dir = temp_dir();
        let file = dir.join("atuin_test_db_");
        let filename = file.to_str().unwrap();
        format!("sqlite://{filename}")
    });

    let mut url = Url::parse(&db_uri)?;
    let cfg = Config::default();
    let mut generator = Generator::from(cfg, 0);
    let snowflake = generator.get();

    let unique_path = format!("{}{snowflake}", url.path());
    url.set_path(&unique_path);

    let db_uri = url.to_string();

    Ok(DbSettings {
        db_uri,
        read_db_uri: None,
    })
}

#[allow(dead_code)]
pub async fn create_test_db() -> eyre::Result<DbSettings> {
    let var = env::var("ATUIN_TEST_DB_URI").ok();
    let settings = get_settings(var)?;

    match settings.db_type() {
        DbType::Postgres => sqlx::Postgres::create_database(&settings.db_uri).await?,
        DbType::Sqlite => sqlx::Sqlite::create_database(&settings.db_uri).await?,
        atuin_server_database::DbType::Unknown => todo!(),
    };

    Ok(settings)
}

#[allow(dead_code)]
pub async fn destroy_test_db(settings: &DbSettings) -> eyre::Result<()> {
    match settings.db_type() {
        DbType::Postgres => sqlx::Postgres::drop_database(&settings.db_uri).await?,
        DbType::Sqlite => sqlx::Sqlite::drop_database(&settings.db_uri).await?,
        DbType::Unknown => todo!(),
    };
    Ok(())
}

#[cfg(test)]
mod tests {
    use regex::Regex;
    use rstest::rstest;

    use super::*;

    #[rstest]
    #[case::none(None, r"sqlite://.*[\\/]atuin_test_db_\d+")]
    #[case::with_param(
        Some("postgres://user:pass@host/database_?mode=ssl".into()),
        r"postgres://user:pass@host/database_\d+\?mode=ssl"
    )]
    fn settings(#[case] input: Option<String>, #[case] pattern: &str) -> eyre::Result<()> {
        let settings = get_settings(input)?;
        let re = Regex::new(pattern)?;
        assert!(re.is_match(&settings.db_uri), "{}", &settings.db_uri);
        Ok(())
    }
}
