//! Database Management CLI Tool
//!
//! This tool helps manage database operations including creation, migration, and seeding.
//!
//! Usage:
//!   cargo run --bin db_tool -- create
//!   cargo run --bin db_tool -- migrate
//!   cargo run --bin db_tool -- reset
//!   cargo run --bin db_tool -- seed
//!   cargo run --bin db_tool -- status

use server::config::Config;
use server::migrations::MigrationRunner;
use server::utils;
use sqlx::{PgPool, Postgres, Sqlite, SqlitePool, migrate::MigrateDatabase};
use std::env;
use std::process;
use tracing::{error, info, warn};
use tracing_subscriber;

#[derive(Debug)]
enum Command {
    Create,
    Drop,
    Migrate,
    Reset,
    Seed,
    Status,
    Rollback { steps: Option<i32> },
}

#[derive(Debug)]
enum DatabaseType {
    PostgreSQL,
    SQLite,
}

#[derive(Debug)]
struct DatabaseInfo {
    db_type: DatabaseType,
    url: String,
    name: String,
}

fn main() {
    // Initialize logging
    tracing_subscriber::fmt()
        .with_env_filter("db_tool=debug,sqlx=info")
        .init();

    // Initialize path utilities
    utils::init();

    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        print_usage();
        process::exit(1);
    }

    let command = match parse_command(&args[1..]) {
        Ok(cmd) => cmd,
        Err(err) => {
            error!("Error parsing command: {}", err);
            print_usage();
            process::exit(1);
        }
    };

    // Load configuration
    let config = match Config::load() {
        Ok(config) => config,
        Err(err) => {
            error!("Failed to load configuration: {}", err);
            process::exit(1);
        }
    };
    let db_info = match parse_database_url(&config.database.url) {
        Ok(info) => info,
        Err(err) => {
            error!("Failed to parse database URL: {}", err);
            process::exit(1);
        }
    };

    info!("Database type: {:?}", db_info.db_type);
    info!("Database name: {}", db_info.name);

    // Create async runtime
    let rt = tokio::runtime::Runtime::new().unwrap();

    let result = rt.block_on(async {
        match command {
            Command::Create => create_database(&db_info).await,
            Command::Drop => drop_database(&db_info).await,
            Command::Migrate => run_migrations(&db_info).await,
            Command::Reset => reset_database(&db_info).await,
            Command::Seed => seed_database(&db_info).await,
            Command::Status => show_migration_status(&db_info).await,
            Command::Rollback { steps } => rollback_migrations(&db_info, steps).await,
        }
    });

    match result {
        Ok(_) => {
            info!("Operation completed successfully");
        }
        Err(err) => {
            error!("Operation failed: {}", err);
            process::exit(1);
        }
    }
}

fn parse_command(args: &[String]) -> Result<Command, String> {
    if args.is_empty() {
        return Err("No command provided".to_string());
    }

    match args[0].as_str() {
        "create" => Ok(Command::Create),
        "drop" => Ok(Command::Drop),
        "migrate" => Ok(Command::Migrate),
        "reset" => Ok(Command::Reset),
        "seed" => Ok(Command::Seed),
        "status" => Ok(Command::Status),
        "rollback" => {
            let steps = if args.len() > 1 {
                args[1].parse::<i32>().ok()
            } else {
                None
            };
            Ok(Command::Rollback { steps })
        }
        _ => Err(format!("Unknown command: {}", args[0])),
    }
}

fn parse_database_url(url: &str) -> Result<DatabaseInfo, String> {
    if url.starts_with("postgresql://") || url.starts_with("postgres://") {
        let name = url
            .split('/')
            .last()
            .unwrap_or("unknown")
            .split('?')
            .next()
            .unwrap_or("unknown")
            .to_string();

        Ok(DatabaseInfo {
            db_type: DatabaseType::PostgreSQL,
            url: url.to_string(),
            name,
        })
    } else if url.starts_with("sqlite://") {
        let path = url.strip_prefix("sqlite://").unwrap_or(url);
        let name = std::path::Path::new(path)
            .file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("unknown")
            .to_string();

        Ok(DatabaseInfo {
            db_type: DatabaseType::SQLite,
            url: url.to_string(),
            name,
        })
    } else {
        Err("Unsupported database URL format".to_string())
    }
}

async fn create_database(db_info: &DatabaseInfo) -> Result<(), Box<dyn std::error::Error>> {
    info!("Creating database: {}", db_info.name);

    match db_info.db_type {
        DatabaseType::PostgreSQL => {
            if !Postgres::database_exists(&db_info.url).await? {
                info!("Database does not exist, creating...");
                Postgres::create_database(&db_info.url).await?;
                info!(
                    "PostgreSQL database '{}' created successfully",
                    db_info.name
                );
            } else {
                info!("PostgreSQL database '{}' already exists", db_info.name);
            }
        }
        DatabaseType::SQLite => {
            if !Sqlite::database_exists(&db_info.url).await? {
                info!("Database does not exist, creating...");
                Sqlite::create_database(&db_info.url).await?;
                info!("SQLite database '{}' created successfully", db_info.name);
            } else {
                info!("SQLite database '{}' already exists", db_info.name);
            }
        }
    }

    Ok(())
}

async fn drop_database(db_info: &DatabaseInfo) -> Result<(), Box<dyn std::error::Error>> {
    warn!("Dropping database: {}", db_info.name);

    // Confirmation prompt
    print!(
        "Are you sure you want to drop the database '{}'? This action cannot be undone. (y/N): ",
        db_info.name
    );
    use std::io::{self, Write};
    io::stdout().flush()?;

    let mut input = String::new();
    io::stdin().read_line(&mut input)?;

    if !input.trim().to_lowercase().starts_with('y') {
        info!("Database drop cancelled");
        return Ok(());
    }

    match db_info.db_type {
        DatabaseType::PostgreSQL => {
            if Postgres::database_exists(&db_info.url).await? {
                Postgres::drop_database(&db_info.url).await?;
                info!(
                    "PostgreSQL database '{}' dropped successfully",
                    db_info.name
                );
            } else {
                warn!("PostgreSQL database '{}' does not exist", db_info.name);
            }
        }
        DatabaseType::SQLite => {
            if Sqlite::database_exists(&db_info.url).await? {
                Sqlite::drop_database(&db_info.url).await?;
                info!("SQLite database '{}' dropped successfully", db_info.name);
            } else {
                warn!("SQLite database '{}' does not exist", db_info.name);
            }
        }
    }

    Ok(())
}

async fn run_migrations(db_info: &DatabaseInfo) -> Result<(), Box<dyn std::error::Error>> {
    info!("Running migrations for database: {}", db_info.name);

    // Ensure database exists
    create_database(db_info).await?;

    let migration_runner = MigrationRunner::new();

    match db_info.db_type {
        DatabaseType::PostgreSQL => {
            let pool = PgPool::connect(&db_info.url).await?;
            migration_runner.run_postgres_migrations(&pool).await?;
            pool.close().await;
        }
        DatabaseType::SQLite => {
            let pool = SqlitePool::connect(&db_info.url).await?;
            migration_runner.run_sqlite_migrations(&pool).await?;
            pool.close().await;
        }
    }

    info!("Migrations completed successfully");
    Ok(())
}

async fn reset_database(db_info: &DatabaseInfo) -> Result<(), Box<dyn std::error::Error>> {
    info!("Resetting database: {}", db_info.name);

    // Drop and recreate
    drop_database(db_info).await?;
    create_database(db_info).await?;
    run_migrations(db_info).await?;

    info!("Database reset completed");
    Ok(())
}

async fn seed_database(db_info: &DatabaseInfo) -> Result<(), Box<dyn std::error::Error>> {
    info!("Seeding database: {}", db_info.name);

    // Load seed data from files
    let seed_files = get_seed_files()?;

    if seed_files.is_empty() {
        info!("No seed files found in seeds/ directory");
        return Ok(());
    }

    match db_info.db_type {
        DatabaseType::PostgreSQL => {
            let pool = PgPool::connect(&db_info.url).await?;
            for seed_file in seed_files {
                if seed_file.contains("postgres")
                    || (!seed_file.contains("sqlite") && !seed_file.contains("postgres"))
                {
                    info!("Running seed file: {}", seed_file);
                    let sql = utils::read_file_from_root(&format!("seeds/{}", seed_file))?;
                    sqlx::raw_sql(&sql).execute(&pool).await?;
                }
            }
            pool.close().await;
        }
        DatabaseType::SQLite => {
            let pool = SqlitePool::connect(&db_info.url).await?;
            for seed_file in seed_files {
                if seed_file.contains("sqlite")
                    || (!seed_file.contains("sqlite") && !seed_file.contains("postgres"))
                {
                    info!("Running seed file: {}", seed_file);
                    let sql = utils::read_file_from_root(&format!("seeds/{}", seed_file))?;
                    sqlx::raw_sql(&sql).execute(&pool).await?;
                }
            }
            pool.close().await;
        }
    }

    info!("Database seeding completed");
    Ok(())
}

async fn show_migration_status(db_info: &DatabaseInfo) -> Result<(), Box<dyn std::error::Error>> {
    info!("Checking migration status for database: {}", db_info.name);

    let migration_runner = MigrationRunner::new();
    let available_migrations = migration_runner.get_migrations();

    info!("Available migrations:");
    for migration in available_migrations {
        info!("  {} - {}", migration.version, migration.name);
    }

    // Check which migrations have been applied
    match db_info.db_type {
        DatabaseType::PostgreSQL => {
            if let Ok(pool) = PgPool::connect(&db_info.url).await {
                let applied = migration_runner
                    .get_applied_migrations_postgres(&pool)
                    .await?;
                info!("Applied migrations: {:?}", applied);
                pool.close().await;
            } else {
                warn!("Could not connect to database to check applied migrations");
            }
        }
        DatabaseType::SQLite => {
            if let Ok(pool) = SqlitePool::connect(&db_info.url).await {
                let applied = migration_runner
                    .get_applied_migrations_sqlite(&pool)
                    .await?;
                info!("Applied migrations: {:?}", applied);
                pool.close().await;
            } else {
                warn!("Could not connect to database to check applied migrations");
            }
        }
    }

    Ok(())
}

async fn rollback_migrations(
    db_info: &DatabaseInfo,
    _steps: Option<i32>,
) -> Result<(), Box<dyn std::error::Error>> {
    warn!("Migration rollback is not yet implemented");
    warn!("Database: {}", db_info.name);
    warn!("This feature requires implementing rollback SQL scripts");
    Ok(())
}

fn get_seed_files() -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let seeds_dir = utils::resolve_from_root("seeds");

    if !seeds_dir.exists() {
        return Ok(vec![]);
    }

    let mut files = Vec::new();
    for entry in std::fs::read_dir(seeds_dir)? {
        let entry = entry?;
        if entry.file_type()?.is_file() {
            if let Some(filename) = entry.file_name().to_str() {
                if filename.ends_with(".sql") {
                    files.push(filename.to_string());
                }
            }
        }
    }

    files.sort();
    Ok(files)
}

fn print_usage() {
    println!("Database Management Tool");
    println!();
    println!("Usage: cargo run --bin db_tool -- <COMMAND>");
    println!();
    println!("Commands:");
    println!("  create               Create the database");
    println!("  drop                 Drop the database (with confirmation)");
    println!("  migrate              Run pending migrations");
    println!("  reset                Drop, create, and migrate database");
    println!("  seed                 Seed database with test data");
    println!("  status               Show migration status");
    println!("  rollback [steps]     Rollback migrations (not yet implemented)");
    println!();
    println!("Examples:");
    println!("  cargo run --bin db_tool -- create");
    println!("  cargo run --bin db_tool -- migrate");
    println!("  cargo run --bin db_tool -- reset");
    println!("  cargo run --bin db_tool -- seed");
    println!();
    println!("Environment:");
    println!("  DATABASE_URL         Override database URL from config");
    println!("  ENVIRONMENT          Set environment (dev/prod)");
}
