//! Comprehensive transpile matrix tests.
//! Tests all 34x34 dialect pair combinations across multiple SQL patterns.
//! Generated via macros for complete coverage.

use altimate_core::SqlDialect;
use altimate_core::polyglot::transpiler::{transpile, transpile_batch};

// ─── Core test macro ────────────────────────────────────────────────

macro_rules! transpile_test {
    ($name:ident, $src:expr, $tgt:expr, $sql:expr) => {
        #[test]
        fn $name() {
            let result = transpile($sql, $src, $tgt);
            assert_eq!(result.original_sql, $sql);
            assert_eq!(result.source_dialect, $src.to_string());
            assert_eq!(result.target_dialect, $tgt.to_string());
            if result.success {
                assert!(
                    !result.transpiled_sql.is_empty(),
                    "Successful transpile from {} to {} should produce output for: {}",
                    $src,
                    $tgt,
                    $sql
                );
                assert!(result.error.is_none());
            }
        }
    };
}

// ─── Macro to generate tests for all 34x34 dialect pairs ───────────

macro_rules! transpile_all_pairs {
    ($prefix:ident, $sql:expr) => {
        paste::paste! {
            // All 34 dialects as sources, each to all 34 targets
            transpile_all_pairs!(@expand $prefix, $sql,
                Generic, Snowflake, Postgres, BigQuery, Databricks, DuckDB,
                Athena, ClickHouse, CockroachDB, DataFusion, Doris, Dremio,
                Drill, Druid, Dune, Exasol, Fabric, Hive, Materialize, MySQL,
                Oracle, Presto, Redshift, RisingWave, SingleStore, Solr, Spark,
                SQLite, StarRocks, Tableau, Teradata, TiDB, Trino, TSQL
            );
        }
    };
    (@expand $prefix:ident, $sql:expr, $($dialect:ident),+) => {
        $(
            transpile_all_pairs!(@targets $prefix, $sql, $dialect,
                Generic, Snowflake, Postgres, BigQuery, Databricks, DuckDB,
                Athena, ClickHouse, CockroachDB, DataFusion, Doris, Dremio,
                Drill, Druid, Dune, Exasol, Fabric, Hive, Materialize, MySQL,
                Oracle, Presto, Redshift, RisingWave, SingleStore, Solr, Spark,
                SQLite, StarRocks, Tableau, Teradata, TiDB, Trino, TSQL
            );
        )+
    };
    (@targets $prefix:ident, $sql:expr, $src:ident, $($tgt:ident),+) => {
        $(
            paste::paste! {
                transpile_test!(
                    [<$prefix _ $src:lower _to_ $tgt:lower>],
                    SqlDialect::$src,
                    SqlDialect::$tgt,
                    $sql
                );
            }
        )+
    };
}

// ─── Pattern 1: Simple SELECT (34x34 = 1156 tests) ─────────────────

mod select_pattern {
    use super::*;
    transpile_all_pairs!(select, "SELECT id, name FROM users WHERE age > 25");
}

// ─── Pattern 2: JOIN query (34x34 = 1156 tests) ────────────────────

mod join_pattern {
    use super::*;
    transpile_all_pairs!(
        join,
        "SELECT u.id, o.amount FROM users u JOIN orders o ON u.id = o.user_id"
    );
}

// ─── Pattern 3: Aggregation (34x34 = 1156 tests) ───────────────────

mod agg_pattern {
    use super::*;
    transpile_all_pairs!(agg, "SELECT COUNT(*) AS cnt FROM orders GROUP BY status");
}

// ─── Identity transpilation: same source and target (34 tests) ──────

macro_rules! identity_test {
    ($($dialect:ident),+ $(,)?) => {
        $(
            paste::paste! {
                #[test]
                fn [<identity_ $dialect:lower>]() {
                    let sql = "SELECT id, name FROM users WHERE id = 1";
                    let result = transpile(sql, SqlDialect::$dialect, SqlDialect::$dialect);
                    assert_eq!(result.original_sql, sql);
                    assert_eq!(result.source_dialect, SqlDialect::$dialect.to_string());
                    assert_eq!(result.target_dialect, SqlDialect::$dialect.to_string());
                    if result.success {
                        assert!(!result.transpiled_sql.is_empty());
                        assert!(result.error.is_none());
                    }
                }
            }
        )+
    };
}

mod identity {
    use super::*;
    identity_test!(
        Generic,
        Snowflake,
        Postgres,
        BigQuery,
        Databricks,
        DuckDB,
        Athena,
        ClickHouse,
        CockroachDB,
        DataFusion,
        Doris,
        Dremio,
        Drill,
        Druid,
        Dune,
        Exasol,
        Fabric,
        Hive,
        Materialize,
        MySQL,
        Oracle,
        Presto,
        Redshift,
        RisingWave,
        SingleStore,
        Solr,
        Spark,
        SQLite,
        StarRocks,
        Tableau,
        Teradata,
        TiDB,
        Trino,
        TSQL
    );
}

// ─── Batch transpilation tests ──────────────────────────────────────

mod batch {
    use super::*;

    macro_rules! batch_test {
        ($name:ident, $src:expr, $tgt:expr) => {
            #[test]
            fn $name() {
                let queries = vec![
                    "SELECT id FROM users",
                    "SELECT name FROM products WHERE price > 10",
                    "SELECT COUNT(*) FROM orders",
                ];
                let results = transpile_batch(&queries, $src, $tgt);
                assert_eq!(results.len(), 3, "batch should return one result per query");
                for (i, r) in results.iter().enumerate() {
                    assert_eq!(r.original_sql, queries[i]);
                    assert_eq!(r.source_dialect, $src.to_string());
                    assert_eq!(r.target_dialect, $tgt.to_string());
                }
            }
        };
    }

    batch_test!(
        batch_generic_to_postgres,
        SqlDialect::Generic,
        SqlDialect::Postgres
    );
    batch_test!(
        batch_generic_to_snowflake,
        SqlDialect::Generic,
        SqlDialect::Snowflake
    );
    batch_test!(
        batch_generic_to_bigquery,
        SqlDialect::Generic,
        SqlDialect::BigQuery
    );
    batch_test!(
        batch_generic_to_mysql,
        SqlDialect::Generic,
        SqlDialect::MySQL
    );
    batch_test!(
        batch_generic_to_duckdb,
        SqlDialect::Generic,
        SqlDialect::DuckDB
    );
    batch_test!(
        batch_generic_to_databricks,
        SqlDialect::Generic,
        SqlDialect::Databricks
    );
    batch_test!(
        batch_generic_to_redshift,
        SqlDialect::Generic,
        SqlDialect::Redshift
    );
    batch_test!(batch_generic_to_tsql, SqlDialect::Generic, SqlDialect::TSQL);
    batch_test!(
        batch_postgres_to_snowflake,
        SqlDialect::Postgres,
        SqlDialect::Snowflake
    );
    batch_test!(
        batch_postgres_to_bigquery,
        SqlDialect::Postgres,
        SqlDialect::BigQuery
    );
    batch_test!(
        batch_mysql_to_postgres,
        SqlDialect::MySQL,
        SqlDialect::Postgres
    );
    batch_test!(
        batch_snowflake_to_bigquery,
        SqlDialect::Snowflake,
        SqlDialect::BigQuery
    );
    batch_test!(
        batch_bigquery_to_postgres,
        SqlDialect::BigQuery,
        SqlDialect::Postgres
    );
    batch_test!(
        batch_duckdb_to_postgres,
        SqlDialect::DuckDB,
        SqlDialect::Postgres
    );
    batch_test!(
        batch_spark_to_databricks,
        SqlDialect::Spark,
        SqlDialect::Databricks
    );
    batch_test!(batch_presto_to_trino, SqlDialect::Presto, SqlDialect::Trino);
    batch_test!(
        batch_oracle_to_postgres,
        SqlDialect::Oracle,
        SqlDialect::Postgres
    );
    batch_test!(
        batch_tsql_to_postgres,
        SqlDialect::TSQL,
        SqlDialect::Postgres
    );
    batch_test!(batch_hive_to_spark, SqlDialect::Hive, SqlDialect::Spark);
    batch_test!(
        batch_clickhouse_to_generic,
        SqlDialect::ClickHouse,
        SqlDialect::Generic
    );

    #[test]
    fn batch_empty_input() {
        let queries: Vec<&str> = vec![];
        let results = transpile_batch(&queries, SqlDialect::Generic, SqlDialect::Postgres);
        assert!(results.is_empty());
    }

    #[test]
    fn batch_single_query() {
        let queries = vec!["SELECT 1"];
        let results = transpile_batch(&queries, SqlDialect::Generic, SqlDialect::Postgres);
        assert_eq!(results.len(), 1);
    }
}

// ─── Error handling tests ───────────────────────────────────────────

mod error_handling {
    use super::*;

    macro_rules! invalid_sql_test {
        ($name:ident, $dialect:expr, $sql:expr) => {
            #[test]
            fn $name() {
                let result = transpile($sql, $dialect, SqlDialect::Generic);
                // Must not panic; metadata preserved regardless of success
                assert_eq!(result.original_sql, $sql);
                assert_eq!(result.source_dialect, $dialect.to_string());
                assert_eq!(result.target_dialect, "generic");
            }
        };
    }

    invalid_sql_test!(invalid_generic, SqlDialect::Generic, "SELECTZ * FROM");
    invalid_sql_test!(invalid_postgres, SqlDialect::Postgres, "SELECTZ * FROM");
    invalid_sql_test!(invalid_snowflake, SqlDialect::Snowflake, "SELECTZ * FROM");
    invalid_sql_test!(invalid_bigquery, SqlDialect::BigQuery, "SELECTZ * FROM");
    invalid_sql_test!(invalid_mysql, SqlDialect::MySQL, "SELECTZ * FROM");
    invalid_sql_test!(invalid_duckdb, SqlDialect::DuckDB, "SELECTZ * FROM");
    invalid_sql_test!(invalid_tsql, SqlDialect::TSQL, "SELECTZ * FROM");
    invalid_sql_test!(invalid_oracle, SqlDialect::Oracle, "SELECTZ * FROM");
    invalid_sql_test!(invalid_spark, SqlDialect::Spark, "SELECTZ * FROM");
    invalid_sql_test!(invalid_hive, SqlDialect::Hive, "SELECTZ * FROM");
    invalid_sql_test!(invalid_redshift, SqlDialect::Redshift, "SELECTZ * FROM");
    invalid_sql_test!(invalid_presto, SqlDialect::Presto, "SELECTZ * FROM");
    invalid_sql_test!(invalid_trino, SqlDialect::Trino, "SELECTZ * FROM");
    invalid_sql_test!(invalid_clickhouse, SqlDialect::ClickHouse, "SELECTZ * FROM");
    invalid_sql_test!(invalid_databricks, SqlDialect::Databricks, "SELECTZ * FROM");
    invalid_sql_test!(invalid_sqlite, SqlDialect::SQLite, "SELECTZ * FROM");

    invalid_sql_test!(empty_generic, SqlDialect::Generic, "");
    invalid_sql_test!(empty_postgres, SqlDialect::Postgres, "");
    invalid_sql_test!(empty_snowflake, SqlDialect::Snowflake, "");
    invalid_sql_test!(empty_mysql, SqlDialect::MySQL, "");

    invalid_sql_test!(whitespace_generic, SqlDialect::Generic, "   ");
    invalid_sql_test!(whitespace_postgres, SqlDialect::Postgres, "   ");

    invalid_sql_test!(semicolons_only, SqlDialect::Generic, ";;;");
    invalid_sql_test!(garbage_text, SqlDialect::Generic, "not sql at all 123 @#$");
}

// ─── Dialect-specific SQL feature tests ─────────────────────────────

mod dialect_specific {
    use super::*;

    #[test]
    fn snowflake_qualify_to_generic() {
        let sql = "SELECT id, name FROM users QUALIFY ROW_NUMBER() OVER (ORDER BY id) = 1";
        let result = transpile(sql, SqlDialect::Snowflake, SqlDialect::Generic);
        assert_eq!(result.original_sql, sql);
        assert_eq!(result.source_dialect, "snowflake");
    }

    #[test]
    fn snowflake_qualify_to_postgres() {
        let sql = "SELECT id, name FROM users QUALIFY ROW_NUMBER() OVER (ORDER BY id) = 1";
        let result = transpile(sql, SqlDialect::Snowflake, SqlDialect::Postgres);
        assert_eq!(result.source_dialect, "snowflake");
        assert_eq!(result.target_dialect, "postgres");
    }

    #[test]
    fn mysql_backtick_to_postgres() {
        let sql = "SELECT `id`, `name` FROM `users`";
        let result = transpile(sql, SqlDialect::MySQL, SqlDialect::Postgres);
        assert_eq!(result.original_sql, sql);
        assert_eq!(result.source_dialect, "mysql");
    }

    #[test]
    fn mysql_backtick_to_snowflake() {
        let sql = "SELECT `id`, `name` FROM `users`";
        let result = transpile(sql, SqlDialect::MySQL, SqlDialect::Snowflake);
        assert_eq!(result.source_dialect, "mysql");
    }

    #[test]
    fn bigquery_backtick_table_to_postgres() {
        let sql = "SELECT id FROM `project.dataset.table`";
        let result = transpile(sql, SqlDialect::BigQuery, SqlDialect::Postgres);
        assert_eq!(result.source_dialect, "bigquery");
        assert_eq!(result.target_dialect, "postgres");
    }

    #[test]
    fn bigquery_backtick_table_to_snowflake() {
        let sql = "SELECT id FROM `project.dataset.table`";
        let result = transpile(sql, SqlDialect::BigQuery, SqlDialect::Snowflake);
        assert_eq!(result.source_dialect, "bigquery");
    }

    #[test]
    fn tsql_top_to_postgres() {
        let sql = "SELECT TOP 10 id, name FROM users";
        let result = transpile(sql, SqlDialect::TSQL, SqlDialect::Postgres);
        assert_eq!(result.source_dialect, "tsql");
        assert_eq!(result.target_dialect, "postgres");
    }

    #[test]
    fn tsql_top_to_mysql() {
        let sql = "SELECT TOP 10 id, name FROM users";
        let result = transpile(sql, SqlDialect::TSQL, SqlDialect::MySQL);
        assert_eq!(result.source_dialect, "tsql");
    }

    #[test]
    fn tsql_top_to_snowflake() {
        let sql = "SELECT TOP 10 id, name FROM users";
        let result = transpile(sql, SqlDialect::TSQL, SqlDialect::Snowflake);
        assert_eq!(result.source_dialect, "tsql");
    }

    #[test]
    fn oracle_rownum_to_postgres() {
        let sql = "SELECT id FROM users WHERE ROWNUM <= 10";
        let result = transpile(sql, SqlDialect::Oracle, SqlDialect::Postgres);
        assert_eq!(result.source_dialect, "oracle");
        assert_eq!(result.target_dialect, "postgres");
    }

    #[test]
    fn oracle_rownum_to_mysql() {
        let sql = "SELECT id FROM users WHERE ROWNUM <= 10";
        let result = transpile(sql, SqlDialect::Oracle, SqlDialect::MySQL);
        assert_eq!(result.source_dialect, "oracle");
    }

    #[test]
    fn postgres_limit_offset_to_snowflake() {
        let sql = "SELECT id FROM users ORDER BY id LIMIT 10 OFFSET 5";
        let result = transpile(sql, SqlDialect::Postgres, SqlDialect::Snowflake);
        assert_eq!(result.source_dialect, "postgres");
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn postgres_limit_offset_to_tsql() {
        let sql = "SELECT id FROM users ORDER BY id LIMIT 10 OFFSET 5";
        let result = transpile(sql, SqlDialect::Postgres, SqlDialect::TSQL);
        assert_eq!(result.source_dialect, "postgres");
    }

    #[test]
    fn generic_cte_to_postgres() {
        let sql = "WITH active AS (SELECT id FROM users WHERE active = true) SELECT id FROM active";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_cte_to_mysql() {
        let sql = "WITH active AS (SELECT id FROM users WHERE active = true) SELECT id FROM active";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::MySQL);
        assert_eq!(result.source_dialect, "generic");
    }

    #[test]
    fn generic_subquery_to_snowflake() {
        let sql = "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Snowflake);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_window_to_bigquery() {
        let sql = "SELECT id, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY id) AS rn FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::BigQuery);
        assert_eq!(result.source_dialect, "generic");
    }

    #[test]
    fn generic_window_to_postgres() {
        let sql = "SELECT id, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY id) AS rn FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_union_to_postgres() {
        let sql = "SELECT id FROM users UNION ALL SELECT id FROM orders";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_union_to_snowflake() {
        let sql = "SELECT id FROM users UNION ALL SELECT id FROM orders";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Snowflake);
        assert_eq!(result.source_dialect, "generic");
    }

    #[test]
    fn generic_case_when_to_postgres() {
        let sql = "SELECT CASE WHEN age > 18 THEN 'adult' ELSE 'minor' END AS category FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_case_when_to_bigquery() {
        let sql = "SELECT CASE WHEN age > 18 THEN 'adult' ELSE 'minor' END AS category FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::BigQuery);
        assert_eq!(result.source_dialect, "generic");
    }

    #[test]
    fn generic_multi_join_to_postgres() {
        let sql = "SELECT u.id, o.amount, p.name FROM users u JOIN orders o ON u.id = o.user_id JOIN products p ON o.product_id = p.id";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_left_join_to_snowflake() {
        let sql = "SELECT u.id, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Snowflake);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_having_to_postgres() {
        let sql =
            "SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id HAVING COUNT(*) > 5";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_having_to_bigquery() {
        let sql =
            "SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id HAVING COUNT(*) > 5";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::BigQuery);
        assert_eq!(result.source_dialect, "generic");
    }

    #[test]
    fn generic_order_by_to_mysql() {
        let sql = "SELECT id, name FROM users ORDER BY name ASC, id DESC";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::MySQL);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_exists_to_postgres() {
        let sql = "SELECT id FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_between_to_snowflake() {
        let sql = "SELECT id FROM users WHERE age BETWEEN 18 AND 65";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Snowflake);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_like_to_postgres() {
        let sql = "SELECT id, name FROM users WHERE name LIKE '%smith%'";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_in_list_to_mysql() {
        let sql = "SELECT id FROM users WHERE status IN ('active', 'pending', 'trial')";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::MySQL);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_is_null_to_postgres() {
        let sql = "SELECT id FROM users WHERE email IS NULL";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_distinct_to_snowflake() {
        let sql = "SELECT DISTINCT status FROM orders";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Snowflake);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_coalesce_to_postgres() {
        let sql = "SELECT COALESCE(name, 'unknown') AS display_name FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }

    #[test]
    fn generic_cast_to_bigquery() {
        let sql = "SELECT CAST(id AS VARCHAR) FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::BigQuery);
        assert_eq!(result.source_dialect, "generic");
    }

    #[test]
    fn generic_aggregate_multiple_to_postgres() {
        let sql = "SELECT MIN(age), MAX(age), AVG(age), SUM(age) FROM users";
        let result = transpile(sql, SqlDialect::Generic, SqlDialect::Postgres);
        if result.success {
            assert!(!result.transpiled_sql.is_empty());
        }
    }
}
