//! Comprehensive matrix tests for structural comparison, edge cases, and dialect-specific features.
//!
//! Test sections:
//! 1. Compare identical queries across all dialects (34 x 15 = 510)
//! 2. Compare different queries across all dialects (34 x 10 = 340)
//! 3. Compare whitespace-normalized queries (34 x 10 = 340)
//! 4. Dialect-specific pipeline tests (34 x 20 = 680)
//! 5. Edge case tests (34 x 15 = 510)
//! 6. Cross-function consistency tests (34 x 5 = 170)
//! 7. Transpile then compare (8 pairs x 5 patterns x 5 = 200)
//! 8. Serialization round-trip tests (5 types x 34 = 170)
//!
//! Total: ~2920 tests

use altimate_core::SqlDialect;
use altimate_core::polyglot::comparator::compare_queries;
use altimate_core::polyglot::extractor::extract_metadata;
use altimate_core::polyglot::formatter::format_sql;
use altimate_core::polyglot::prevalidator::prevalidate_syntax;
use altimate_core::polyglot::transpiler::transpile;
use paste::paste;

// ─── Dialect list macro ────────────────────────────────────────────────

macro_rules! all_dialects {
    ($macro_name:ident) => {
        $macro_name!(generic, SqlDialect::Generic);
        $macro_name!(snowflake, SqlDialect::Snowflake);
        $macro_name!(postgres, SqlDialect::Postgres);
        $macro_name!(bigquery, SqlDialect::BigQuery);
        $macro_name!(databricks, SqlDialect::Databricks);
        $macro_name!(duckdb, SqlDialect::DuckDB);
        $macro_name!(athena, SqlDialect::Athena);
        $macro_name!(clickhouse, SqlDialect::ClickHouse);
        $macro_name!(cockroachdb, SqlDialect::CockroachDB);
        $macro_name!(datafusion, SqlDialect::DataFusion);
        $macro_name!(doris, SqlDialect::Doris);
        $macro_name!(dremio, SqlDialect::Dremio);
        $macro_name!(drill, SqlDialect::Drill);
        $macro_name!(druid, SqlDialect::Druid);
        $macro_name!(dune, SqlDialect::Dune);
        $macro_name!(exasol, SqlDialect::Exasol);
        $macro_name!(fabric, SqlDialect::Fabric);
        $macro_name!(hive, SqlDialect::Hive);
        $macro_name!(materialize, SqlDialect::Materialize);
        $macro_name!(mysql, SqlDialect::MySQL);
        $macro_name!(oracle, SqlDialect::Oracle);
        $macro_name!(presto, SqlDialect::Presto);
        $macro_name!(redshift, SqlDialect::Redshift);
        $macro_name!(risingwave, SqlDialect::RisingWave);
        $macro_name!(singlestore, SqlDialect::SingleStore);
        $macro_name!(solr, SqlDialect::Solr);
        $macro_name!(spark, SqlDialect::Spark);
        $macro_name!(sqlite, SqlDialect::SQLite);
        $macro_name!(starrocks, SqlDialect::StarRocks);
        $macro_name!(tableau, SqlDialect::Tableau);
        $macro_name!(teradata, SqlDialect::Teradata);
        $macro_name!(tidb, SqlDialect::TiDB);
        $macro_name!(trino, SqlDialect::Trino);
        $macro_name!(tsql, SqlDialect::TSQL);
    };
}

// ═══════════════════════════════════════════════════════════════════════
// SECTION 1: Compare identical queries across all dialects
//            34 dialects x 15 SQL patterns = 510 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! compare_identical_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = compare_queries($sql, $sql, $dialect);
                assert!(result.is_ok(), "compare should not error for {:?}: {:?}", $dialect, result.err());
                let cmp = result.unwrap();
                assert!(cmp.identical, "Same query should be identical for {:?}: {}", $dialect, $sql);
                assert_eq!(cmp.diff_count, 0, "Same query should have 0 diffs for {:?}", $dialect);
            }
        }
    };
}

/// Variant that tolerates parse errors (for dialect-specific syntax issues like LIMIT)
macro_rules! compare_identical_tolerant_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = compare_queries($sql, $sql, $dialect);
                if let Ok(cmp) = result {
                    assert!(cmp.identical, "Same query should be identical for {:?}: {}", $dialect, $sql);
                    assert_eq!(cmp.diff_count, 0, "Same query should have 0 diffs for {:?}", $dialect);
                }
                // Parse errors are acceptable for some dialect/syntax combos
            }
        }
    };
}

macro_rules! gen_identical_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            compare_identical_test!([<identical_ $dialect_name _simple_select>], $dialect, "SELECT id FROM users");
            compare_identical_test!([<identical_ $dialect_name _select_star>], $dialect, "SELECT * FROM orders");
            compare_identical_test!([<identical_ $dialect_name _select_where>], $dialect, "SELECT id FROM users WHERE age > 25");
            compare_identical_test!([<identical_ $dialect_name _select_multi_col>], $dialect, "SELECT id, name, email FROM users");
            compare_identical_test!([<identical_ $dialect_name _count>], $dialect, "SELECT COUNT(*) FROM users");
            compare_identical_test!([<identical_ $dialect_name _sum>], $dialect, "SELECT SUM(amount) FROM orders");
            compare_identical_test!([<identical_ $dialect_name _join>], $dialect, "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id");
            compare_identical_test!([<identical_ $dialect_name _left_join>], $dialect, "SELECT a.id FROM t1 a LEFT JOIN t2 b ON a.id = b.id");
            compare_identical_test!([<identical_ $dialect_name _order_by>], $dialect, "SELECT id FROM users ORDER BY name");
            compare_identical_test!([<identical_ $dialect_name _group_by>], $dialect, "SELECT status, COUNT(*) FROM orders GROUP BY status");
            compare_identical_tolerant_test!([<identical_ $dialect_name _limit>], $dialect, "SELECT id FROM users LIMIT 10");
            compare_identical_test!([<identical_ $dialect_name _subquery>], $dialect, "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)");
            compare_identical_test!([<identical_ $dialect_name _alias>], $dialect, "SELECT u.id, u.name FROM users u");
            compare_identical_test!([<identical_ $dialect_name _between>], $dialect, "SELECT id FROM users WHERE age BETWEEN 18 AND 65");
            compare_identical_test!([<identical_ $dialect_name _literal>], $dialect, "SELECT 1");
        }
    };
}

all_dialects!(gen_identical_tests_for_dialect);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 2: Compare different queries across all dialects
//            34 dialects x 10 pairs = 340 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! compare_different_test {
    ($name:ident, $dialect:expr, $left:expr, $right:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = compare_queries($left, $right, $dialect);
                assert!(result.is_ok(), "compare should not error for {:?}: {:?}", $dialect, result.err());
                let cmp = result.unwrap();
                assert!(!cmp.identical, "Different queries should not be identical for {:?}: {} vs {}", $dialect, $left, $right);
                assert!(cmp.diff_count > 0, "Different queries should have diffs for {:?}", $dialect);
            }
        }
    };
}

/// Variant that tolerates parse errors (for dialect-specific syntax issues like LIMIT)
macro_rules! compare_different_tolerant_test {
    ($name:ident, $dialect:expr, $left:expr, $right:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = compare_queries($left, $right, $dialect);
                if let Ok(cmp) = result {
                    assert!(!cmp.identical, "Different queries should not be identical for {:?}: {} vs {}", $dialect, $left, $right);
                    assert!(cmp.diff_count > 0, "Different queries should have diffs for {:?}", $dialect);
                }
                // Parse errors are acceptable for some dialect/syntax combos
            }
        }
    };
}

macro_rules! gen_different_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            compare_different_test!([<different_ $dialect_name _diff_cols>], $dialect,
                "SELECT id FROM users", "SELECT name FROM users");
            compare_different_test!([<different_ $dialect_name _diff_tables>], $dialect,
                "SELECT id FROM users", "SELECT id FROM orders");
            compare_different_test!([<different_ $dialect_name _diff_where>], $dialect,
                "SELECT id FROM users WHERE age > 25", "SELECT id FROM users WHERE age < 18");
            compare_different_test!([<different_ $dialect_name _diff_agg>], $dialect,
                "SELECT COUNT(*) FROM users", "SELECT SUM(amount) FROM orders");
            compare_different_test!([<different_ $dialect_name _diff_order>], $dialect,
                "SELECT id FROM users ORDER BY id", "SELECT id FROM users ORDER BY name");
            compare_different_tolerant_test!([<different_ $dialect_name _diff_limit>], $dialect,
                "SELECT id FROM users LIMIT 10", "SELECT id FROM users LIMIT 20");
            compare_different_test!([<different_ $dialect_name _diff_join_type>], $dialect,
                "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id",
                "SELECT a.id FROM t1 a LEFT JOIN t2 b ON a.id = b.id");
            compare_different_test!([<different_ $dialect_name _diff_col_count>], $dialect,
                "SELECT id, name FROM users", "SELECT id FROM users");
            compare_different_test!([<different_ $dialect_name _diff_star_tables>], $dialect,
                "SELECT * FROM users", "SELECT * FROM orders");
            compare_different_test!([<different_ $dialect_name _diff_cte_vs_plain>], $dialect,
                "WITH cte AS (SELECT 1) SELECT * FROM cte", "SELECT 1");
        }
    };
}

all_dialects!(gen_different_tests_for_dialect);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 3: Compare whitespace-normalized queries
//            34 dialects x 10 patterns = 340 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! compare_whitespace_test {
    ($name:ident, $dialect:expr, $compact:expr, $spaced:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = compare_queries($compact, $spaced, $dialect);
                assert!(result.is_ok(), "compare should not error for {:?}: {:?}", $dialect, result.err());
                let cmp = result.unwrap();
                assert!(cmp.identical,
                    "Whitespace-different queries should be identical for {:?}. compact={}, spaced={}, diffs={:?}",
                    $dialect, $compact, $spaced, cmp.diffs);
            }
        }
    };
}

macro_rules! gen_whitespace_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            compare_whitespace_test!([<whitespace_ $dialect_name _extra_spaces>], $dialect,
                "SELECT id FROM users", "SELECT  id  FROM  users");
            compare_whitespace_test!([<whitespace_ $dialect_name _leading_trailing>], $dialect,
                "SELECT id FROM users", "  SELECT id FROM users  ");
            compare_whitespace_test!([<whitespace_ $dialect_name _tabs>], $dialect,
                "SELECT id FROM users", "SELECT\tid\tFROM\tusers");
            compare_whitespace_test!([<whitespace_ $dialect_name _newlines>], $dialect,
                "SELECT id FROM users", "SELECT\nid\nFROM\nusers");
            compare_whitespace_test!([<whitespace_ $dialect_name _mixed>], $dialect,
                "SELECT id, name FROM users WHERE age > 25",
                "SELECT  id,  name\n  FROM  users\n  WHERE  age > 25");
            compare_whitespace_test!([<whitespace_ $dialect_name _where_spaces>], $dialect,
                "SELECT id FROM users WHERE id=1",
                "SELECT id FROM users WHERE id = 1");
            compare_whitespace_test!([<whitespace_ $dialect_name _join_newlines>], $dialect,
                "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id",
                "SELECT a.id\nFROM t1 a\nJOIN t2 b\nON a.id = b.id");
            compare_whitespace_test!([<whitespace_ $dialect_name _comma_spaces>], $dialect,
                "SELECT id,name,email FROM users",
                "SELECT id, name, email FROM users");
            compare_whitespace_test!([<whitespace_ $dialect_name _group_by_spaces>], $dialect,
                "SELECT status, COUNT(*) FROM orders GROUP BY status",
                "SELECT status,COUNT(*) FROM orders GROUP BY status");
            compare_whitespace_test!([<whitespace_ $dialect_name _order_newline>], $dialect,
                "SELECT id FROM users ORDER BY id",
                "SELECT id FROM users\nORDER BY id");
        }
    };
}

all_dialects!(gen_whitespace_tests_for_dialect);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 4: Dialect-specific pipeline tests
//            34 dialects x 20 SQL patterns = 680 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! dialect_pipeline_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        paste! {
            #[test]
            fn $name() {
                // Run through format, prevalidate, extract — must not panic
                let fmt = format_sql($sql, $dialect);
                let _ = fmt;
                let pre = prevalidate_syntax($sql, $dialect);
                let _ = pre;
                let ext = extract_metadata($sql, $dialect);
                let _ = ext;
            }
        }
    };
}

macro_rules! gen_pipeline_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            dialect_pipeline_test!([<pipeline_ $dialect_name _simple_select>], $dialect,
                "SELECT id FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _select_star>], $dialect,
                "SELECT * FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _select_where>], $dialect,
                "SELECT id FROM users WHERE age > 25");
            dialect_pipeline_test!([<pipeline_ $dialect_name _select_multi_col>], $dialect,
                "SELECT id, name, email FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _count>], $dialect,
                "SELECT COUNT(*) FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _sum>], $dialect,
                "SELECT SUM(amount) FROM orders");
            dialect_pipeline_test!([<pipeline_ $dialect_name _avg>], $dialect,
                "SELECT AVG(price) FROM products");
            dialect_pipeline_test!([<pipeline_ $dialect_name _min_max>], $dialect,
                "SELECT MIN(age), MAX(age) FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _inner_join>], $dialect,
                "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id");
            dialect_pipeline_test!([<pipeline_ $dialect_name _left_join>], $dialect,
                "SELECT a.id FROM t1 a LEFT JOIN t2 b ON a.id = b.id");
            dialect_pipeline_test!([<pipeline_ $dialect_name _right_join>], $dialect,
                "SELECT a.id FROM t1 a RIGHT JOIN t2 b ON a.id = b.id");
            dialect_pipeline_test!([<pipeline_ $dialect_name _group_by>], $dialect,
                "SELECT status, COUNT(*) FROM orders GROUP BY status");
            dialect_pipeline_test!([<pipeline_ $dialect_name _having>], $dialect,
                "SELECT status, COUNT(*) cnt FROM orders GROUP BY status HAVING COUNT(*) > 5");
            dialect_pipeline_test!([<pipeline_ $dialect_name _order_by>], $dialect,
                "SELECT id FROM users ORDER BY name ASC");
            dialect_pipeline_test!([<pipeline_ $dialect_name _limit>], $dialect,
                "SELECT id FROM users LIMIT 10");
            dialect_pipeline_test!([<pipeline_ $dialect_name _subquery>], $dialect,
                "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)");
            dialect_pipeline_test!([<pipeline_ $dialect_name _case_when>], $dialect,
                "SELECT CASE WHEN age > 18 THEN 'adult' ELSE 'minor' END FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _coalesce>], $dialect,
                "SELECT COALESCE(name, 'unknown') FROM users");
            dialect_pipeline_test!([<pipeline_ $dialect_name _between>], $dialect,
                "SELECT id FROM users WHERE age BETWEEN 18 AND 65");
            dialect_pipeline_test!([<pipeline_ $dialect_name _like>], $dialect,
                "SELECT id FROM users WHERE name LIKE '%smith%'");
        }
    };
}

all_dialects!(gen_pipeline_tests_for_dialect);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 5: Edge case tests
//            34 dialects x 15 patterns = 510 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! edge_case_no_panic_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        paste! {
            #[test]
            fn $name() {
                // Edge cases: none of these should panic. Errors are acceptable.
                let fmt = format_sql($sql, $dialect);
                let _ = fmt;
                let pre = prevalidate_syntax($sql, $dialect);
                let _ = pre;
                let ext = extract_metadata($sql, $dialect);
                let _ = ext;
                // compare with itself — also must not panic
                let cmp = compare_queries($sql, $sql, $dialect);
                let _ = cmp;
            }
        }
    };
}

// Generate the long column list at compile time
const LONG_COL_LIST: &str = "SELECT c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, c23, c24, c25, c26, c27, c28, c29, c30, c31, c32, c33, c34, c35, c36, c37, c38, c39, c40, c41, c42, c43, c44, c45, c46, c47, c48, c49, c50 FROM t";

const LARGE_IN_LIST: &str = "SELECT id FROM users WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50)";

macro_rules! gen_edge_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            edge_case_no_panic_test!([<edge_ $dialect_name _empty>], $dialect, "");
            edge_case_no_panic_test!([<edge_ $dialect_name _whitespace>], $dialect, "   ");
            edge_case_no_panic_test!([<edge_ $dialect_name _keyword_only>], $dialect, "SELECT");
            edge_case_no_panic_test!([<edge_ $dialect_name _long_col_list>], $dialect, LONG_COL_LIST);
            edge_case_no_panic_test!([<edge_ $dialect_name _nested_subquery>], $dialect,
                "SELECT id FROM (SELECT id FROM (SELECT id FROM users) a) b");
            edge_case_no_panic_test!([<edge_ $dialect_name _escaped_quote>], $dialect,
                "SELECT id FROM users WHERE name = 'O''Brien'");
            edge_case_no_panic_test!([<edge_ $dialect_name _unicode>], $dialect,
                "SELECT id FROM users WHERE name = 'Jose'");
            edge_case_no_panic_test!([<edge_ $dialect_name _multi_stmt>], $dialect,
                "SELECT 1; SELECT 2; SELECT 3");
            edge_case_no_panic_test!([<edge_ $dialect_name _block_comment>], $dialect,
                "SELECT id /* comment */ FROM users");
            edge_case_no_panic_test!([<edge_ $dialect_name _line_comment>], $dialect,
                "SELECT id -- comment\nFROM users");
            edge_case_no_panic_test!([<edge_ $dialect_name _null>], $dialect,
                "SELECT NULL, id FROM users WHERE name IS NULL");
            edge_case_no_panic_test!([<edge_ $dialect_name _booleans>], $dialect,
                "SELECT id FROM users WHERE active = TRUE AND deleted = FALSE");
            edge_case_no_panic_test!([<edge_ $dialect_name _numerics>], $dialect,
                "SELECT 1, 2.5, -3, 1e10 FROM users");
            edge_case_no_panic_test!([<edge_ $dialect_name _string_newlines>], $dialect,
                "SELECT id FROM users WHERE bio LIKE '%test%'");
            edge_case_no_panic_test!([<edge_ $dialect_name _large_in_list>], $dialect, LARGE_IN_LIST);
        }
    };
}

all_dialects!(gen_edge_tests_for_dialect);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 6: Cross-function consistency tests
//            34 dialects x 5 patterns = 170 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! cross_function_test {
    ($name:ident, $dialect:expr, $sql:expr) => {
        paste! {
            #[test]
            fn $name() {
                // Format a query, then extract from the formatted output.
                // If format succeeds, extraction from the formatted SQL should also succeed.
                let fmt = format_sql($sql, $dialect);
                if fmt.success && !fmt.formatted_sql.is_empty() {
                    let ext_original = extract_metadata($sql, $dialect);
                    let ext_formatted = extract_metadata(&fmt.formatted_sql, $dialect);
                    // Both should succeed
                    assert!(ext_original.is_ok(),
                        "extract from original should work for {:?}", $dialect);
                    assert!(ext_formatted.is_ok(),
                        "extract from formatted should work for {:?}: formatted={}",
                        $dialect, fmt.formatted_sql);
                    let orig = ext_original.unwrap();
                    let fmtd = ext_formatted.unwrap();
                    // Table references should be preserved
                    assert_eq!(orig.tables, fmtd.tables,
                        "Tables should match between original and formatted for {:?}", $dialect);
                }
            }
        }
    };
}

macro_rules! gen_cross_function_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            cross_function_test!([<cross_ $dialect_name _simple>], $dialect,
                "SELECT id FROM users");
            cross_function_test!([<cross_ $dialect_name _join>], $dialect,
                "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id");
            cross_function_test!([<cross_ $dialect_name _agg>], $dialect,
                "SELECT COUNT(*), SUM(amount) FROM orders");
            cross_function_test!([<cross_ $dialect_name _subquery>], $dialect,
                "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)");
            cross_function_test!([<cross_ $dialect_name _group_having>], $dialect,
                "SELECT status, COUNT(*) FROM orders GROUP BY status HAVING COUNT(*) > 5");
        }
    };
}

all_dialects!(gen_cross_function_tests_for_dialect);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 7: Transpile then compare
//            8 dialect pairs x 5 patterns x 5 variants = 200 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! transpile_compare_test {
    ($name:ident, $source:expr, $target:expr, $sql:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = transpile($sql, $source, $target);
                // If transpilation succeeds, compare original with transpiled
                if result.success && !result.transpiled_sql.is_empty() {
                    let transpiled = &result.transpiled_sql[0];
                    // Compare using the target dialect
                    let cmp = compare_queries($sql, transpiled, $target);
                    // Just verify it doesn't error — the queries may or may not be identical
                    // depending on the dialect transformation
                    let _ = cmp;
                }
            }
        }
    };
}

macro_rules! gen_transpile_compare_for_pair {
    ($src_name:ident, $tgt_name:ident, $source:expr, $target:expr) => {
        paste! {
            transpile_compare_test!([<transpile_cmp_ $src_name _to_ $tgt_name _simple>],
                $source, $target, "SELECT id FROM users");
            transpile_compare_test!([<transpile_cmp_ $src_name _to_ $tgt_name _where>],
                $source, $target, "SELECT id FROM users WHERE age > 25");
            transpile_compare_test!([<transpile_cmp_ $src_name _to_ $tgt_name _join>],
                $source, $target, "SELECT a.id FROM t1 a JOIN t2 b ON a.id = b.id");
            transpile_compare_test!([<transpile_cmp_ $src_name _to_ $tgt_name _agg>],
                $source, $target, "SELECT COUNT(*) FROM orders GROUP BY status");
            transpile_compare_test!([<transpile_cmp_ $src_name _to_ $tgt_name _subquery>],
                $source, $target, "SELECT id FROM users WHERE id IN (SELECT user_id FROM orders)");
        }
    };
}

// 8 representative dialect pairs
gen_transpile_compare_for_pair!(generic, postgres, SqlDialect::Generic, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(generic, mysql, SqlDialect::Generic, SqlDialect::MySQL);
gen_transpile_compare_for_pair!(
    generic,
    snowflake,
    SqlDialect::Generic,
    SqlDialect::Snowflake
);
gen_transpile_compare_for_pair!(generic, bigquery, SqlDialect::Generic, SqlDialect::BigQuery);
gen_transpile_compare_for_pair!(postgres, mysql, SqlDialect::Postgres, SqlDialect::MySQL);
gen_transpile_compare_for_pair!(
    postgres,
    snowflake,
    SqlDialect::Postgres,
    SqlDialect::Snowflake
);
gen_transpile_compare_for_pair!(mysql, postgres, SqlDialect::MySQL, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    snowflake,
    postgres,
    SqlDialect::Snowflake,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(
    bigquery,
    postgres,
    SqlDialect::BigQuery,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(
    databricks,
    postgres,
    SqlDialect::Databricks,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(duckdb, postgres, SqlDialect::DuckDB, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    clickhouse,
    postgres,
    SqlDialect::ClickHouse,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(trino, postgres, SqlDialect::Trino, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(presto, postgres, SqlDialect::Presto, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    redshift,
    postgres,
    SqlDialect::Redshift,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(spark, postgres, SqlDialect::Spark, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(sqlite, postgres, SqlDialect::SQLite, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(hive, postgres, SqlDialect::Hive, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(oracle, postgres, SqlDialect::Oracle, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(tsql, postgres, SqlDialect::TSQL, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(athena, postgres, SqlDialect::Athena, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    cockroachdb,
    postgres,
    SqlDialect::CockroachDB,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(
    datafusion,
    postgres,
    SqlDialect::DataFusion,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(doris, postgres, SqlDialect::Doris, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(dremio, postgres, SqlDialect::Dremio, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(drill, postgres, SqlDialect::Drill, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(druid, postgres, SqlDialect::Druid, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(dune, postgres, SqlDialect::Dune, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(exasol, postgres, SqlDialect::Exasol, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(fabric, postgres, SqlDialect::Fabric, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    materialize,
    postgres,
    SqlDialect::Materialize,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(
    risingwave,
    postgres,
    SqlDialect::RisingWave,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(
    singlestore,
    postgres,
    SqlDialect::SingleStore,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(solr, postgres, SqlDialect::Solr, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    starrocks,
    postgres,
    SqlDialect::StarRocks,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(tableau, postgres, SqlDialect::Tableau, SqlDialect::Postgres);
gen_transpile_compare_for_pair!(
    teradata,
    postgres,
    SqlDialect::Teradata,
    SqlDialect::Postgres
);
gen_transpile_compare_for_pair!(tidb, postgres, SqlDialect::TiDB, SqlDialect::Postgres);

// Additional cross-dialect pairs (not to postgres)
gen_transpile_compare_for_pair!(mysql, snowflake, SqlDialect::MySQL, SqlDialect::Snowflake);
gen_transpile_compare_for_pair!(
    snowflake,
    bigquery,
    SqlDialect::Snowflake,
    SqlDialect::BigQuery
);
gen_transpile_compare_for_pair!(bigquery, mysql, SqlDialect::BigQuery, SqlDialect::MySQL);
gen_transpile_compare_for_pair!(trino, snowflake, SqlDialect::Trino, SqlDialect::Snowflake);

// ═══════════════════════════════════════════════════════════════════════
// SECTION 8: Serialization round-trip tests
//            5 result types x 34 dialects = 170 tests
// ═══════════════════════════════════════════════════════════════════════

macro_rules! serde_roundtrip_compare {
    ($name:ident, $dialect:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = compare_queries("SELECT id FROM users", "SELECT name FROM users", $dialect);
                if let Ok(cmp) = result {
                    let json = serde_json::to_string(&cmp).expect("CompareResult should serialize");
                    let deser: altimate_core::polyglot::types::CompareResult =
                        serde_json::from_str(&json).expect("CompareResult should deserialize");
                    assert_eq!(cmp.identical, deser.identical);
                    assert_eq!(cmp.diff_count, deser.diff_count);
                    assert_eq!(cmp.diffs.len(), deser.diffs.len());
                }
            }
        }
    };
}

macro_rules! serde_roundtrip_format {
    ($name:ident, $dialect:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = format_sql("SELECT id FROM users", $dialect);
                let json = serde_json::to_string(&result).expect("FormatResult should serialize");
                let deser: altimate_core::polyglot::types::FormatResult =
                    serde_json::from_str(&json).expect("FormatResult should deserialize");
                assert_eq!(result.success, deser.success);
                assert_eq!(result.original_sql, deser.original_sql);
                assert_eq!(result.dialect, deser.dialect);
            }
        }
    };
}

macro_rules! serde_roundtrip_extract {
    ($name:ident, $dialect:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = extract_metadata("SELECT id FROM users", $dialect);
                if let Ok(ext) = result {
                    let json = serde_json::to_string(&ext).expect("ExtractResult should serialize");
                    let deser: altimate_core::polyglot::types::ExtractResult =
                        serde_json::from_str(&json).expect("ExtractResult should deserialize");
                    assert_eq!(ext.tables, deser.tables);
                    assert_eq!(ext.columns, deser.columns);
                    assert_eq!(ext.node_count, deser.node_count);
                }
            }
        }
    };
}

macro_rules! serde_roundtrip_prevalidate {
    ($name:ident, $dialect:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = prevalidate_syntax("SELECT id FROM users", $dialect);
                let json = serde_json::to_string(&result).expect("PrevalidateResult should serialize");
                let deser: altimate_core::polyglot::types::PrevalidateResult =
                    serde_json::from_str(&json).expect("PrevalidateResult should deserialize");
                assert_eq!(result.valid, deser.valid);
                assert_eq!(result.errors.len(), deser.errors.len());
            }
        }
    };
}

macro_rules! serde_roundtrip_transpile {
    ($name:ident, $dialect:expr) => {
        paste! {
            #[test]
            fn $name() {
                let result = transpile("SELECT id FROM users", $dialect, SqlDialect::Generic);
                let json = serde_json::to_string(&result).expect("TranspileResult should serialize");
                let deser: altimate_core::polyglot::types::TranspileResult =
                    serde_json::from_str(&json).expect("TranspileResult should deserialize");
                assert_eq!(result.success, deser.success);
                assert_eq!(result.original_sql, deser.original_sql);
                assert_eq!(result.source_dialect, deser.source_dialect);
            }
        }
    };
}

macro_rules! gen_serde_tests_for_dialect {
    ($dialect_name:ident, $dialect:expr) => {
        paste! {
            serde_roundtrip_compare!([<serde_compare_ $dialect_name>], $dialect);
            serde_roundtrip_format!([<serde_format_ $dialect_name>], $dialect);
            serde_roundtrip_extract!([<serde_extract_ $dialect_name>], $dialect);
            serde_roundtrip_prevalidate!([<serde_prevalidate_ $dialect_name>], $dialect);
            serde_roundtrip_transpile!([<serde_transpile_ $dialect_name>], $dialect);
        }
    };
}

all_dialects!(gen_serde_tests_for_dialect);
