//! Comprehensive tests for the migration analysis module.

use altimate_core::migration::analyzer::analyze_migration;
use altimate_core::migration::types::MigrationRisk;
use altimate_core::schema::types::{SchemaDefinition, SqlDialect};
use std::collections::HashMap;

// ─── Helper ──────────────────────────────────────────────────────

fn empty_schema() -> SchemaDefinition {
    SchemaDefinition {
        version: "1".to_string(),
        dialect: SqlDialect::Generic,
        tables: HashMap::new(),
        database: None,
        schema_name: None,
        glossary: None,
    }
}

// ─── DROP TABLE ──────────────────────────────────────────────────

#[test]
fn test_drop_table_is_destructive() {
    let r = analyze_migration("DROP TABLE users;", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Destructive);
    assert!(!r.safe);
}

#[test]
fn test_drop_table_finding_contains_table_name() {
    let r = analyze_migration("DROP TABLE users;", &empty_schema());
    assert!(r.findings[0].operation.contains("users"));
}

#[test]
fn test_drop_multiple_tables() {
    let r = analyze_migration("DROP TABLE users; DROP TABLE orders;", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Destructive);
    assert_eq!(r.findings.len(), 2);
}

// ─── DROP COLUMN ─────────────────────────────────────────────────

#[test]
fn test_drop_column_is_destructive() {
    let r = analyze_migration("ALTER TABLE users DROP COLUMN email;", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Destructive);
    assert!(!r.safe);
}

#[test]
fn test_drop_column_has_mitigation() {
    let r = analyze_migration("ALTER TABLE users DROP COLUMN email;", &empty_schema());
    assert!(r.findings[0].mitigation.is_some());
}

// ─── ADD COLUMN ──────────────────────────────────────────────────

#[test]
fn test_add_column_with_default_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE users ADD COLUMN age INT DEFAULT 0;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
    assert!(r.safe);
}

#[test]
fn test_add_column_nullable_is_safe() {
    let r = analyze_migration("ALTER TABLE users ADD COLUMN bio TEXT;", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
    assert!(r.safe);
}

#[test]
fn test_add_not_null_column_without_default_is_dangerous() {
    let r = analyze_migration(
        "ALTER TABLE users ADD COLUMN age INT NOT NULL;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Dangerous);
    assert!(!r.safe);
}

#[test]
fn test_add_not_null_column_with_default_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE users ADD COLUMN age INT NOT NULL DEFAULT 0;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
    assert!(r.safe);
}

#[test]
fn test_add_column_rollback_generated() {
    let r = analyze_migration(
        "ALTER TABLE users ADD COLUMN age INT DEFAULT 0;",
        &empty_schema(),
    );
    assert!(r.rollback_sql.is_some());
    assert!(r.rollback_sql.unwrap().contains("DROP COLUMN age"));
}

// ─── SET NOT NULL ────────────────────────────────────────────────

#[test]
fn test_set_not_null_is_dangerous() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN email SET NOT NULL;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Dangerous);
}

#[test]
fn test_set_not_null_has_rollback() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN email SET NOT NULL;",
        &empty_schema(),
    );
    assert!(r.rollback_sql.is_some());
    assert!(r.rollback_sql.unwrap().contains("DROP NOT NULL"));
}

// ─── DROP NOT NULL ───────────────────────────────────────────────

#[test]
fn test_drop_not_null_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN email DROP NOT NULL;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
    assert!(r.safe);
}

// ─── ALTER COLUMN TYPE ───────────────────────────────────────────

#[test]
fn test_alter_type_to_bigint_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN age SET DATA TYPE BIGINT;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
}

#[test]
fn test_alter_type_to_text_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN name SET DATA TYPE TEXT;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
}

#[test]
fn test_alter_type_to_smallint_is_dangerous() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN age SET DATA TYPE SMALLINT;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Dangerous);
}

#[test]
fn test_alter_type_to_boolean_is_dangerous() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN age SET DATA TYPE BOOLEAN;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Dangerous);
}

#[test]
fn test_alter_type_to_varchar_is_caution() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN name SET DATA TYPE VARCHAR(50);",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Caution);
}

// ─── RENAME COLUMN ───────────────────────────────────────────────

#[test]
fn test_rename_column_is_dangerous() {
    let r = analyze_migration(
        "ALTER TABLE users RENAME COLUMN email TO email_address;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Dangerous);
}

#[test]
fn test_rename_column_has_rollback() {
    let r = analyze_migration(
        "ALTER TABLE users RENAME COLUMN email TO email_address;",
        &empty_schema(),
    );
    assert!(r.rollback_sql.is_some());
    let rollback = r.rollback_sql.unwrap();
    assert!(rollback.contains("email_address"));
    assert!(rollback.contains("email"));
}

// ─── CREATE TABLE ────────────────────────────────────────────────

#[test]
fn test_create_table_is_safe() {
    let r = analyze_migration("CREATE TABLE logs (id INT, message TEXT);", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
    assert!(r.safe);
}

#[test]
fn test_create_table_rollback_drops_table() {
    let r = analyze_migration("CREATE TABLE logs (id INT, message TEXT);", &empty_schema());
    assert!(r.rollback_sql.is_some());
    assert!(r.rollback_sql.unwrap().contains("DROP TABLE"));
}

// ─── CREATE INDEX ────────────────────────────────────────────────

#[test]
fn test_create_index_is_safe() {
    let r = analyze_migration("CREATE INDEX idx_email ON users(email);", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
}

#[test]
fn test_create_index_rollback_drops_index() {
    let r = analyze_migration("CREATE INDEX idx_email ON users(email);", &empty_schema());
    assert!(r.rollback_sql.is_some());
    assert!(r.rollback_sql.unwrap().contains("DROP INDEX"));
}

// ─── DROP INDEX ──────────────────────────────────────────────────

#[test]
fn test_drop_index_is_caution() {
    let r = analyze_migration("DROP INDEX idx_email;", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Caution);
    assert!(r.safe); // Caution is still safe
}

// ─── DROP CONSTRAINT ─────────────────────────────────────────────

#[test]
fn test_drop_constraint_is_caution() {
    let r = analyze_migration(
        "ALTER TABLE orders DROP CONSTRAINT fk_user_id;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Caution);
}

// ─── ADD CONSTRAINT ──────────────────────────────────────────────

#[test]
fn test_add_constraint_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
}

// ─── TRUNCATE ────────────────────────────────────────────────────

#[test]
fn test_truncate_is_destructive() {
    let r = analyze_migration("TRUNCATE TABLE users;", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Destructive);
    assert!(!r.safe);
}

// ─── SET DEFAULT / DROP DEFAULT ──────────────────────────────────

#[test]
fn test_set_default_is_safe() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN age SET DEFAULT 18;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
}

#[test]
fn test_drop_default_is_caution() {
    let r = analyze_migration(
        "ALTER TABLE users ALTER COLUMN age DROP DEFAULT;",
        &empty_schema(),
    );
    assert_eq!(r.overall_risk, MigrationRisk::Caution);
}

// ─── Multiple statements ─────────────────────────────────────────

#[test]
fn test_multiple_statements_highest_risk_wins() {
    let sql = "CREATE TABLE t (id INT); DROP TABLE users;";
    let r = analyze_migration(sql, &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Destructive);
}

#[test]
fn test_multiple_safe_statements() {
    let sql = "CREATE TABLE a (id INT); CREATE TABLE b (id INT);";
    let r = analyze_migration(sql, &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
    assert_eq!(r.findings.len(), 2);
}

#[test]
fn test_combined_rollback_sql() {
    let sql = "ALTER TABLE users ADD COLUMN a INT DEFAULT 0; ALTER TABLE users ADD COLUMN b TEXT;";
    let r = analyze_migration(sql, &empty_schema());
    let rollback = r.rollback_sql.expect("should have rollback");
    assert!(rollback.contains("DROP COLUMN a"));
    assert!(rollback.contains("DROP COLUMN b"));
}

// ─── Invalid SQL ─────────────────────────────────────────────────

#[test]
fn test_invalid_sql_returns_destructive() {
    let r = analyze_migration("THIS IS NOT SQL AT ALL", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Destructive);
    assert!(r.findings[0].operation.contains("PARSE ERROR"));
}

// ─── INSERT statement ────────────────────────────────────────────

#[test]
fn test_insert_is_safe() {
    let r = analyze_migration("INSERT INTO users (name) VALUES ('test');", &empty_schema());
    assert_eq!(r.overall_risk, MigrationRisk::Safe);
}

// ─── SQL preservation ────────────────────────────────────────────

#[test]
fn test_original_sql_preserved() {
    let sql = "DROP TABLE users;";
    let r = analyze_migration(sql, &empty_schema());
    assert_eq!(r.sql, sql);
}

// ─── Risk ordering ───────────────────────────────────────────────

#[test]
fn test_risk_ordering() {
    assert!(MigrationRisk::Safe < MigrationRisk::Caution);
    assert!(MigrationRisk::Caution < MigrationRisk::Dangerous);
    assert!(MigrationRisk::Dangerous < MigrationRisk::Destructive);
}
