use anyhow::Context;
use clap::Command;
use migrate::MigratorTrait;
use sea_orm::{Database, DatabaseConnection};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenvy::dotenv().ok();
    config::AppConfig::load();

    let cmd = Command::new("migrate")
        .about("Database migration CLI")
        .arg(
            clap::Arg::new("steps")
                .help("Number of migrations (for up/down)")
                .required(false)
                .index(1),
        )
        .subcommand(Command::new("up").about("Apply pending migrations"))
        .subcommand(Command::new("down").about("Revert applied migrations"))
        .subcommand(Command::new("fresh").about("Drop all tables and re-apply"))
        .subcommand(Command::new("refresh").about("Revert all then re-apply"))
        .subcommand(Command::new("reset").about("Revert all applied migrations"))
        .subcommand(Command::new("status").about("Show migration status"))
        .try_get_matches()
        .map_err(|e| anyhow::anyhow!("{}", e))?;

    let db_url = config::AppConfig::load().database_url()?;

    let db: DatabaseConnection = Database::connect(&db_url).await?;

    match cmd.subcommand_name() {
        Some("up") => {
            let steps = cmd
                .get_one::<String>("steps")
                .and_then(|s| s.parse().ok())
                .unwrap_or(0);
            run_up(&db, steps).await?;
        }
        Some("down") => {
            let steps = cmd
                .get_one::<String>("steps")
                .and_then(|s| s.parse().ok())
                .unwrap_or(1);
            run_down(&db, steps).await?;
        }
        Some("fresh") => run_fresh(&db).await?,
        Some("refresh") => run_refresh(&db).await?,
        Some("reset") => run_reset(&db).await?,
        Some("status") => run_status(&db).await?,
        _ => {
            eprintln!(
                "Usage: migrate <command>\nCommands: up, down, fresh, refresh, reset, status"
            );
            std::process::exit(1);
        }
    }

    Ok(())
}

async fn run_up(db: &DatabaseConnection, steps: u32) -> anyhow::Result<()> {
    migrate::Migrator::up(db, if steps == 0 { None } else { Some(steps) })
        .await
        .context("failed to run migrations up")?;
    Ok(())
}

async fn run_down(db: &DatabaseConnection, steps: u32) -> anyhow::Result<()> {
    migrate::Migrator::down(db, Some(steps))
        .await
        .context("failed to run migrations down")?;
    Ok(())
}

async fn run_fresh(db: &DatabaseConnection) -> anyhow::Result<()> {
    migrate::Migrator::fresh(db)
        .await
        .context("failed to run migrations fresh")?;
    Ok(())
}

async fn run_refresh(db: &DatabaseConnection) -> anyhow::Result<()> {
    migrate::Migrator::refresh(db)
        .await
        .context("failed to run migrations refresh")?;
    Ok(())
}

async fn run_reset(db: &DatabaseConnection) -> anyhow::Result<()> {
    migrate::Migrator::reset(db)
        .await
        .context("failed to run migrations reset")?;
    Ok(())
}

async fn run_status(db: &DatabaseConnection) -> anyhow::Result<()> {
    migrate::Migrator::status(db)
        .await
        .context("failed to get migration status")?;
    Ok(())
}
