//! Format and pre-validate matrix tests.
//!
//! This file generates ~2500+ tests covering:
//! - format_sql across 34 dialects × 40 SQL patterns
//! - Format idempotency (round-trip) across 34 dialects × 5 patterns
//! - prevalidate_syntax (valid) across 34 dialects × 30 patterns
//! - prevalidate_syntax (invalid) across 34 dialects × 5 patterns

use altimate_core::SqlDialect;
use altimate_core::polyglot::formatter::format_sql;
use altimate_core::polyglot::prevalidator::prevalidate_syntax;
use paste::paste;

// ─── Macros ─────────────────────────────────────────────────────────────────

/// Test that format_sql produces a result with correct metadata.
/// If formatting succeeds, the formatted SQL must be non-empty and error must be None.
macro_rules! format_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        #[test]
        fn $name() {
            let result = format_sql($sql, $dialect);
            assert_eq!(result.original_sql, $sql);
            assert_eq!(result.dialect, $dialect.to_string());
            if result.success {
                assert!(
                    !result.formatted_sql.is_empty(),
                    "Formatted SQL should not be empty on success for: {}",
                    $sql
                );
                assert!(
                    result.error.is_none(),
                    "Error should be None on success for: {}",
                    $sql
                );
            }
        }
    };
}

/// Test that formatting is idempotent: format(format(sql)) == format(sql).
macro_rules! format_idempotent_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        #[test]
        fn $name() {
            let first = format_sql($sql, $dialect);
            if !first.success {
                return; // skip idempotency check if initial format fails
            }
            let second = format_sql(&first.formatted_sql, $dialect);
            assert!(
                second.success,
                "Second format should succeed for dialect {:?}, sql: {}",
                $dialect, $sql
            );
            assert_eq!(
                first.formatted_sql, second.formatted_sql,
                "Format should be idempotent for dialect {:?}, sql: {}",
                $dialect, $sql
            );
        }
    };
}

/// Test that valid SQL passes pre-validation.
macro_rules! prevalidate_valid_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        #[test]
        fn $name() {
            let result = prevalidate_syntax($sql, $dialect);
            assert!(
                result.valid,
                "Expected valid SQL for dialect {:?}: {} -- errors: {:?}",
                $dialect, $sql, result.errors
            );
        }
    };
}

/// Test that invalid SQL fails pre-validation.
macro_rules! prevalidate_invalid_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        #[test]
        fn $name() {
            let result = prevalidate_syntax($sql, $dialect);
            assert!(
                !result.valid,
                "Expected invalid SQL for dialect {:?}: {}",
                $dialect, $sql
            );
            assert!(
                !result.errors.is_empty(),
                "Expected at least one error for dialect {:?}: {}",
                $dialect,
                $sql
            );
        }
    };
}

// ─── Dialect expansion macros ───────────────────────────────────────────────

/// Expand a format test across all 34 dialects.
macro_rules! format_all_dialects {
    ($prefix:ident, $sql:expr, [$($dialect:ident),* $(,)?]) => {
        $(
            paste! {
                format_test!(
                    [<fmt_ $prefix _ $dialect:lower>],
                    SqlDialect::$dialect,
                    $sql
                );
            }
        )*
    };
}

/// Expand a format idempotency test across all 34 dialects.
macro_rules! format_idempotent_all_dialects {
    ($prefix:ident, $sql:expr, [$($dialect:ident),* $(,)?]) => {
        $(
            paste! {
                format_idempotent_test!(
                    [<fmt_idem_ $prefix _ $dialect:lower>],
                    SqlDialect::$dialect,
                    $sql
                );
            }
        )*
    };
}

/// Expand a prevalidate-valid test across all 34 dialects.
macro_rules! prevalidate_valid_all_dialects {
    ($prefix:ident, $sql:expr, [$($dialect:ident),* $(,)?]) => {
        $(
            paste! {
                prevalidate_valid_test!(
                    [<pv_valid_ $prefix _ $dialect:lower>],
                    SqlDialect::$dialect,
                    $sql
                );
            }
        )*
    };
}

/// Expand a prevalidate-invalid test across all 34 dialects.
macro_rules! prevalidate_invalid_all_dialects {
    ($prefix:ident, $sql:expr, [$($dialect:ident),* $(,)?]) => {
        $(
            paste! {
                prevalidate_invalid_test!(
                    [<pv_invalid_ $prefix _ $dialect:lower>],
                    SqlDialect::$dialect,
                    $sql
                );
            }
        )*
    };
}

// ─── All dialects list ──────────────────────────────────────────────────────

macro_rules! all_dialects {
    ($macro_name:ident, $prefix:ident, $sql:expr) => {
        $macro_name!(
            $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,
            ]
        );
    };
}

// ═══════════════════════════════════════════════════════════════════════════
// FORMAT TESTS: 34 dialects × 40 patterns = 1360 tests
// ═══════════════════════════════════════════════════════════════════════════

// --- Basic SELECT patterns ---
all_dialects!(
    format_all_dialects,
    select_single_col,
    "SELECT id FROM users"
);
all_dialects!(
    format_all_dialects,
    select_multi_col,
    "SELECT id, name, email FROM users"
);
all_dialects!(format_all_dialects, select_star, "SELECT * FROM users");
all_dialects!(format_all_dialects, select_literal, "SELECT 1");
all_dialects!(
    format_all_dialects,
    select_where,
    "SELECT id FROM users WHERE age > 25"
);
all_dialects!(
    format_all_dialects,
    select_distinct,
    "SELECT DISTINCT name FROM users"
);
all_dialects!(
    format_all_dialects,
    select_order_by,
    "SELECT id FROM users ORDER BY id"
);
all_dialects!(
    format_all_dialects,
    select_limit,
    "SELECT id FROM users LIMIT 10"
);
all_dialects!(
    format_all_dialects,
    select_string_lit,
    "SELECT id FROM users WHERE name = 'test'"
);
all_dialects!(
    format_all_dialects,
    select_between,
    "SELECT id FROM users WHERE age BETWEEN 18 AND 65"
);

// --- JOIN patterns ---
all_dialects!(
    format_all_dialects,
    join_inner,
    "SELECT u.id FROM users u JOIN orders o ON u.id = o.user_id"
);
all_dialects!(
    format_all_dialects,
    join_left,
    "SELECT u.id FROM users u LEFT JOIN orders o ON u.id = o.user_id"
);
all_dialects!(
    format_all_dialects,
    join_triple,
    "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id JOIN t3 c ON b.id = c.id"
);

// --- Aggregation patterns ---
all_dialects!(
    format_all_dialects,
    agg_count_star,
    "SELECT COUNT(*) FROM users"
);
all_dialects!(
    format_all_dialects,
    agg_group_by,
    "SELECT status, COUNT(*) FROM orders GROUP BY status"
);
all_dialects!(
    format_all_dialects,
    agg_having,
    "SELECT user_id, SUM(amount) FROM orders GROUP BY user_id HAVING SUM(amount) > 100"
);

// --- Window functions ---
all_dialects!(
    format_all_dialects,
    win_row_number,
    "SELECT id, ROW_NUMBER() OVER (ORDER BY id) FROM users"
);
all_dialects!(
    format_all_dialects,
    win_rank,
    "SELECT id, RANK() OVER (PARTITION BY dept ORDER BY salary DESC) FROM emp"
);

// --- CTEs ---
all_dialects!(
    format_all_dialects,
    cte_simple,
    "WITH cte AS (SELECT id FROM users) SELECT id FROM cte"
);
all_dialects!(
    format_all_dialects,
    cte_double,
    "WITH a AS (SELECT 1 AS x), b AS (SELECT 2 AS y) SELECT x, y FROM a, b"
);

// --- Subqueries ---
all_dialects!(
    format_all_dialects,
    subq_in,
    "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)"
);
all_dialects!(
    format_all_dialects,
    subq_scalar,
    "SELECT id, (SELECT COUNT(*) FROM orders WHERE orders.uid = users.id) FROM users"
);

// --- Expressions ---
all_dialects!(
    format_all_dialects,
    expr_case,
    "SELECT CASE WHEN age > 18 THEN 'adult' ELSE 'minor' END FROM users"
);
all_dialects!(
    format_all_dialects,
    expr_coalesce,
    "SELECT COALESCE(name, 'unknown') FROM users"
);
all_dialects!(
    format_all_dialects,
    expr_cast,
    "SELECT CAST(price AS INT) FROM products"
);

// --- Set operations ---
all_dialects!(
    format_all_dialects,
    union_all,
    "SELECT id FROM users UNION ALL SELECT id FROM admins"
);
all_dialects!(
    format_all_dialects,
    union_distinct,
    "SELECT id FROM users UNION SELECT id FROM admins"
);

// --- DML ---
all_dialects!(
    format_all_dialects,
    insert_values,
    "INSERT INTO users (id, name) VALUES (1, 'alice')"
);
all_dialects!(
    format_all_dialects,
    update_set,
    "UPDATE users SET name = 'bob' WHERE id = 1"
);
all_dialects!(
    format_all_dialects,
    delete_from,
    "DELETE FROM users WHERE id = 1"
);

// --- More complex patterns ---
all_dialects!(
    format_all_dialects,
    nested_subq,
    "SELECT * FROM (SELECT id, name FROM users WHERE active = 1) AS sub WHERE sub.id > 5"
);
all_dialects!(
    format_all_dialects,
    multi_cte,
    "WITH x AS (SELECT 1 AS a), y AS (SELECT a + 1 AS b FROM x), z AS (SELECT b + 1 AS c FROM y) SELECT c FROM z"
);
all_dialects!(
    format_all_dialects,
    complex_expr,
    "SELECT a + b * c - d / e AS result FROM t"
);
all_dialects!(
    format_all_dialects,
    like_pattern,
    "SELECT name FROM users WHERE name LIKE '%alice%'"
);
all_dialects!(
    format_all_dialects,
    in_list,
    "SELECT id FROM users WHERE status IN ('active', 'pending', 'review')"
);
all_dialects!(
    format_all_dialects,
    is_null,
    "SELECT id FROM users WHERE email IS NULL"
);
all_dialects!(
    format_all_dialects,
    is_not_null,
    "SELECT id FROM users WHERE email IS NOT NULL"
);
all_dialects!(
    format_all_dialects,
    exists_subq,
    "SELECT id FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)"
);
all_dialects!(
    format_all_dialects,
    not_exists,
    "SELECT id FROM users WHERE NOT EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)"
);
all_dialects!(
    format_all_dialects,
    between_and,
    "SELECT id FROM orders WHERE amount BETWEEN 100 AND 500 AND status = 'paid'"
);

// ═══════════════════════════════════════════════════════════════════════════
// FORMAT IDEMPOTENCY TESTS: 34 dialects × 5 patterns = 170 tests
// ═══════════════════════════════════════════════════════════════════════════

all_dialects!(
    format_idempotent_all_dialects,
    idem_simple,
    "SELECT id FROM users"
);
all_dialects!(
    format_idempotent_all_dialects,
    idem_join,
    "SELECT u.id FROM users u JOIN orders o ON u.id = o.user_id"
);
all_dialects!(
    format_idempotent_all_dialects,
    idem_agg,
    "SELECT COUNT(*) FROM users GROUP BY status"
);
all_dialects!(
    format_idempotent_all_dialects,
    idem_cte,
    "WITH cte AS (SELECT id FROM users) SELECT id FROM cte"
);
all_dialects!(
    format_idempotent_all_dialects,
    idem_union,
    "SELECT id FROM users UNION ALL SELECT id FROM admins"
);

// ═══════════════════════════════════════════════════════════════════════════
// PREVALIDATE VALID TESTS: 34 dialects × 30 patterns = 1020 tests
// ═══════════════════════════════════════════════════════════════════════════

// --- Basic SELECT ---
all_dialects!(
    prevalidate_valid_all_dialects,
    select_one,
    "SELECT id FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    select_multi,
    "SELECT id, name, email FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    select_star,
    "SELECT * FROM users"
);
all_dialects!(prevalidate_valid_all_dialects, select_lit, "SELECT 1");
all_dialects!(
    prevalidate_valid_all_dialects,
    select_where,
    "SELECT id FROM users WHERE age > 25"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    select_distinct,
    "SELECT DISTINCT name FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    select_order,
    "SELECT id FROM users ORDER BY id"
);
// LIMIT is not valid in all dialects (Hive/Spark reject it), so test with a subset.
prevalidate_valid_all_dialects!(
    select_limit,
    "SELECT id FROM users LIMIT 10",
    [
        Generic,
        Snowflake,
        Postgres,
        BigQuery,
        Databricks,
        DuckDB,
        Athena,
        ClickHouse,
        CockroachDB,
        DataFusion,
        Doris,
        Dremio,
        Drill,
        Druid,
        Dune,
        Exasol,
        Fabric,
        Materialize,
        MySQL,
        Oracle,
        Presto,
        Redshift,
        RisingWave,
        SingleStore,
        Solr,
        SQLite,
        StarRocks,
        Tableau,
        Teradata,
        TiDB,
        Trino,
        TSQL,
    ]
);
all_dialects!(
    prevalidate_valid_all_dialects,
    select_string,
    "SELECT id FROM users WHERE name = 'test'"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    select_between,
    "SELECT id FROM users WHERE age BETWEEN 18 AND 65"
);

// --- JOINs ---
all_dialects!(
    prevalidate_valid_all_dialects,
    join_inner,
    "SELECT u.id FROM users u JOIN orders o ON u.id = o.user_id"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    join_left,
    "SELECT u.id FROM users u LEFT JOIN orders o ON u.id = o.user_id"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    join_triple,
    "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id JOIN t3 c ON b.id = c.id"
);

// --- Aggregation ---
all_dialects!(
    prevalidate_valid_all_dialects,
    agg_count,
    "SELECT COUNT(*) FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    agg_group,
    "SELECT status, COUNT(*) FROM orders GROUP BY status"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    agg_having,
    "SELECT user_id, SUM(amount) FROM orders GROUP BY user_id HAVING SUM(amount) > 100"
);

// --- Window functions ---
all_dialects!(
    prevalidate_valid_all_dialects,
    win_row,
    "SELECT id, ROW_NUMBER() OVER (ORDER BY id) FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    win_rank,
    "SELECT id, RANK() OVER (PARTITION BY dept ORDER BY salary DESC) FROM emp"
);

// --- CTEs ---
all_dialects!(
    prevalidate_valid_all_dialects,
    cte_simple,
    "WITH cte AS (SELECT id FROM users) SELECT id FROM cte"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    cte_double,
    "WITH a AS (SELECT 1 AS x), b AS (SELECT 2 AS y) SELECT x, y FROM a, b"
);

// --- Subqueries ---
all_dialects!(
    prevalidate_valid_all_dialects,
    subq_in,
    "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    subq_scalar,
    "SELECT id, (SELECT COUNT(*) FROM orders WHERE orders.uid = users.id) FROM users"
);

// --- Expressions ---
all_dialects!(
    prevalidate_valid_all_dialects,
    expr_case,
    "SELECT CASE WHEN age > 18 THEN 'adult' ELSE 'minor' END FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    expr_coalesce,
    "SELECT COALESCE(name, 'unknown') FROM users"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    expr_cast,
    "SELECT CAST(price AS INT) FROM products"
);

// --- Set operations ---
all_dialects!(
    prevalidate_valid_all_dialects,
    union_all,
    "SELECT id FROM users UNION ALL SELECT id FROM admins"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    union_distinct,
    "SELECT id FROM users UNION SELECT id FROM admins"
);

// --- DML ---
all_dialects!(
    prevalidate_valid_all_dialects,
    insert_vals,
    "INSERT INTO users (id, name) VALUES (1, 'alice')"
);
all_dialects!(
    prevalidate_valid_all_dialects,
    delete_from,
    "DELETE FROM users WHERE id = 1"
);

// ═══════════════════════════════════════════════════════════════════════════
// PREVALIDATE INVALID TESTS: 34 dialects × 5 patterns = 170 tests
// ═══════════════════════════════════════════════════════════════════════════

all_dialects!(
    prevalidate_invalid_all_dialects,
    bad_keyword,
    "SELECTZ * FROM users"
);
// ClickHouse considers "SELECT FROM" valid (dialect quirk), so exclude it.
prevalidate_invalid_all_dialects!(
    missing_cols,
    "SELECT FROM",
    [
        Generic,
        Snowflake,
        Postgres,
        BigQuery,
        Databricks,
        DuckDB,
        Athena,
        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,
    ]
);
all_dialects!(
    prevalidate_invalid_all_dialects,
    incomplete_insert,
    "INSERT INTO"
);
all_dialects!(
    prevalidate_invalid_all_dialects,
    missing_table,
    "SELECT id FROM WHERE"
);
all_dialects!(
    prevalidate_invalid_all_dialects,
    unmatched_paren,
    "SELECT (id FROM users"
);
