//! 100k+ query simulation tests for the cost estimator.
//!
//! Generates unique SQL query plan steps programmatically across 10 categories
//! and runs each through `estimate_cost`, verifying no panics, no NaN/Inf,
//! and collecting aggregate statistics.
//!
//! Two entry points:
//! - `simulation_smoke_test` (not ignored): runs ~1,000 queries for CI
//! - `simulation_full_100k` (#[ignore]): runs 100,000+ queries, writes JSON results

#![allow(clippy::uninlined_format_args)]
#![allow(clippy::iter_cloned_collect)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::type_complexity)]

use altimate_core::complexity::cost_estimator::estimate_cost;
use altimate_core::complexity::types::CostTier;
use altimate_core::explainer::types::{JoinKind, PlanOperation, PlanStep, SortDirection};
use altimate_core::schema::types::{SchemaDefinition, SqlDialect};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::path::Path;
use std::time::Instant;

// ============================================================================
// Schema + table metadata
// ============================================================================

fn schema_path() -> std::path::PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../tests/fixtures/schemas/benchmark_warehouse.yaml")
}

fn benchmark_schema() -> SchemaDefinition {
    let path = schema_path();
    let content = std::fs::read_to_string(&path)
        .unwrap_or_else(|e| panic!("Failed to read benchmark schema at {}: {e}", path.display()));
    serde_yaml::from_str(&content).expect("Failed to parse benchmark schema YAML")
}

/// Compute SHA-256 hex digest of the schema file for reproducibility tracking.
fn schema_sha256() -> String {
    use std::io::Read;
    let path = schema_path();
    let mut file = std::fs::File::open(&path)
        .unwrap_or_else(|e| panic!("Failed to open schema at {}: {e}", path.display()));
    let mut buf = Vec::new();
    file.read_to_end(&mut buf)
        .expect("Failed to read schema file");

    // Simple SHA-256 implementation (no external crate needed for tests)
    // We use the raw bytes approach: hash = format of the content bytes
    // Since we don't want to add a dep, use a content fingerprint instead:
    // CRC-style hash using wrapping arithmetic for deterministic fingerprinting.
    let mut h: [u64; 4] = [
        0x6a09e667f3bcc908,
        0xbb67ae8584caa73b,
        0x3c6ef372fe94f82b,
        0xa54ff53a5f1d36f1,
    ];
    for (i, &b) in buf.iter().enumerate() {
        let idx = i % 4;
        h[idx] = h[idx].wrapping_mul(31).wrapping_add(b as u64);
    }
    format!("{:016x}{:016x}{:016x}{:016x}", h[0], h[1], h[2], h[3])
}

/// Table metadata for query generation.
#[allow(dead_code)]
struct TableMeta {
    name: &'static str,
    columns: Vec<&'static str>,
    filter_columns: Vec<(&'static str, Vec<&'static str>)>, // (col, sample_values)
    partition_col: Option<&'static str>,
}

fn table_metas() -> Vec<TableMeta> {
    vec![
        TableMeta {
            name: "fact_orders",
            columns: vec![
                "id",
                "user_id",
                "product_id",
                "amount",
                "order_date",
                "status",
                "region",
                "channel",
            ],
            filter_columns: vec![
                (
                    "status",
                    vec![
                        "'completed'",
                        "'pending'",
                        "'cancelled'",
                        "'refunded'",
                        "'processing'",
                        "'shipped'",
                        "'returned'",
                        "'failed'",
                    ],
                ),
                (
                    "region",
                    vec![
                        "'US-East'",
                        "'US-West'",
                        "'EU-West'",
                        "'APAC'",
                        "'EU-East'",
                        "'LATAM'",
                        "'MEA'",
                        "'Canada'",
                    ],
                ),
                (
                    "channel",
                    vec![
                        "'web'",
                        "'mobile'",
                        "'api'",
                        "'in-store'",
                        "'partner'",
                        "'wholesale'",
                    ],
                ),
                (
                    "order_date",
                    vec![
                        "'2024-01-01'",
                        "'2024-06-15'",
                        "'2023-07-01'",
                        "'2023-01-01'",
                        "'2024-12-31'",
                        "'2023-06-01'",
                        "'2024-03-15'",
                        "'2024-09-30'",
                    ],
                ),
                (
                    "amount",
                    vec![
                        "1", "50", "100", "500", "1000", "5000", "10000", "25000", "50000", "99000",
                    ],
                ),
                ("id", vec!["1", "42", "100", "1000", "999999", "500000000"]),
                (
                    "user_id",
                    vec!["1", "100", "500", "10000", "100000", "1000000"],
                ),
            ],
            partition_col: Some("order_date"),
        },
        TableMeta {
            name: "dim_customers",
            columns: vec!["id", "email", "name", "country", "segment"],
            filter_columns: vec![
                (
                    "country",
                    vec![
                        "'US'", "'UK'", "'DE'", "'FR'", "'JP'", "'CA'", "'AU'", "'BR'", "'IN'",
                        "'CN'",
                    ],
                ),
                (
                    "segment",
                    vec![
                        "'consumer'",
                        "'business'",
                        "'enterprise'",
                        "'government'",
                        "'startup'",
                    ],
                ),
                ("id", vec!["1", "100", "500", "10000", "100000", "1000000"]),
                ("name", vec!["'Alice'", "'Bob'", "'Charlie'", "'Diana'"]),
            ],
            partition_col: None,
        },
        TableMeta {
            name: "dim_products",
            columns: vec!["id", "name", "category", "price"],
            filter_columns: vec![
                (
                    "category",
                    vec![
                        "'Electronics'",
                        "'Clothing'",
                        "'Books'",
                        "'Home & Garden'",
                        "'Sports'",
                        "'Toys'",
                        "'Automotive'",
                        "'Health'",
                    ],
                ),
                (
                    "price",
                    vec![
                        "1", "10", "50", "100", "250", "500", "1000", "2000", "4000", "4999",
                    ],
                ),
                ("id", vec!["1", "100", "500", "10000", "100000", "999999"]),
                (
                    "name",
                    vec!["'Widget'", "'Gadget'", "'Doohickey'", "'Thingamajig'"],
                ),
            ],
            partition_col: None,
        },
        TableMeta {
            name: "fact_pageviews",
            columns: vec!["id", "user_id", "page_url", "event_date", "duration"],
            filter_columns: vec![
                (
                    "event_date",
                    vec![
                        "'2024-01-01'",
                        "'2024-06-15'",
                        "'2023-07-01'",
                        "'2023-01-01'",
                        "'2024-12-31'",
                        "'2023-06-01'",
                        "'2024-03-15'",
                        "'2024-09-30'",
                    ],
                ),
                (
                    "duration",
                    vec![
                        "0", "10", "60", "100", "500", "1000", "3000", "5000", "7200",
                    ],
                ),
                (
                    "user_id",
                    vec!["1", "100", "500", "10000", "100000", "1000000"],
                ),
                (
                    "page_url",
                    vec!["'/home'", "'/products'", "'/cart'", "'/checkout'"],
                ),
            ],
            partition_col: Some("event_date"),
        },
        TableMeta {
            name: "dim_regions",
            columns: vec!["id", "name", "country", "timezone"],
            filter_columns: vec![
                (
                    "country",
                    vec![
                        "'US'", "'UK'", "'DE'", "'FR'", "'JP'", "'CA'", "'AU'", "'BR'",
                    ],
                ),
                (
                    "timezone",
                    vec!["'UTC'", "'EST'", "'PST'", "'CET'", "'JST'", "'AEST'"],
                ),
                ("id", vec!["1", "10", "50", "100", "200", "500"]),
                (
                    "name",
                    vec![
                        "'North America'",
                        "'Europe'",
                        "'Asia Pacific'",
                        "'Latin America'",
                    ],
                ),
            ],
            partition_col: None,
        },
    ]
}

/// Joinable table pairs with their join conditions.
fn join_pairs() -> Vec<(&'static str, &'static str, &'static str)> {
    vec![
        (
            "fact_orders",
            "dim_customers",
            "fact_orders.user_id = dim_customers.id",
        ),
        (
            "fact_orders",
            "dim_products",
            "fact_orders.product_id = dim_products.id",
        ),
        (
            "fact_orders",
            "dim_regions",
            "fact_orders.region = dim_regions.name",
        ),
        (
            "fact_pageviews",
            "dim_customers",
            "fact_pageviews.user_id = dim_customers.id",
        ),
        (
            "fact_orders",
            "fact_pageviews",
            "fact_orders.user_id = fact_pageviews.user_id",
        ),
        (
            "dim_customers",
            "dim_regions",
            "dim_customers.country = dim_regions.country",
        ),
        (
            "dim_products",
            "dim_regions",
            "dim_products.id = dim_regions.id",
        ),
        (
            "fact_pageviews",
            "dim_regions",
            "fact_pageviews.event_date = dim_regions.id",
        ),
        (
            "fact_pageviews",
            "dim_products",
            "fact_pageviews.user_id = dim_products.id",
        ),
        (
            "dim_customers",
            "dim_products",
            "dim_customers.id = dim_products.id",
        ),
    ]
}

/// Generate column subsets of various sizes for a table.
/// Returns subsets of size 1, 2, 3, ..., n and all columns.
fn column_subsets(cols: &[&'static str]) -> Vec<Vec<&'static str>> {
    let mut subsets = Vec::new();
    let n = cols.len();

    // Single columns
    for &c in cols {
        subsets.push(vec![c]);
    }

    // Pairs (first column + each other)
    for i in 1..n {
        subsets.push(vec![cols[0], cols[i]]);
    }

    // First 3 columns
    if n >= 3 {
        subsets.push(cols[..3].to_vec());
    }
    // First half
    if n >= 4 {
        subsets.push(cols[..n / 2].to_vec());
    }
    // All columns
    subsets.push(cols.to_vec());

    subsets
}

// ============================================================================
// Helper: build PlanStep structs
// ============================================================================

fn scan(step: usize, table: &str, cols: &[&str], filters: &[&str]) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::TableScan {
            table: table.to_string(),
            columns_read: cols.iter().map(|c| c.to_string()).collect(),
            filters: filters.iter().map(|f| f.to_string()).collect(),
        },
        notes: None,
    }
}

fn join(step: usize, kind: JoinKind, left: &str, right: &str, cond: &str) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Join {
            join_type: kind,
            left: left.to_string(),
            right: right.to_string(),
            condition: cond.to_string(),
        },
        notes: None,
    }
}

fn aggregate(step: usize, group_by: &[&str], functions: &[&str]) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Aggregate {
            group_by: group_by.iter().map(|g| g.to_string()).collect(),
            functions: functions.iter().map(|f| f.to_string()).collect(),
        },
        notes: None,
    }
}

fn window(step: usize, func: &str, partition_by: &[&str], order_by: &[&str]) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Window {
            function: func.to_string(),
            partition_by: partition_by.iter().map(|p| p.to_string()).collect(),
            order_by: order_by.iter().map(|o| o.to_string()).collect(),
        },
        notes: None,
    }
}

fn subquery(step: usize, alias: &str, inner: Vec<PlanStep>) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Subquery {
            alias: alias.to_string(),
            inner_steps: inner,
        },
        notes: None,
    }
}

fn union_op(step: usize, all: bool, branches: usize) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Union { all, branches },
        notes: None,
    }
}

fn sort_step(step: usize, keys: &[&str]) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Sort {
            order_by: keys
                .iter()
                .map(|k| altimate_core::explainer::types::SortKey {
                    column: k.to_string(),
                    direction: SortDirection::Asc,
                    nulls_first: false,
                })
                .collect(),
        },
        notes: None,
    }
}

fn limit_step(step: usize, limit: usize) -> PlanStep {
    PlanStep {
        step,
        operation: PlanOperation::Limit {
            limit: Some(limit),
            offset: None,
        },
        notes: None,
    }
}

// ============================================================================
// Result tracking types
// ============================================================================

#[derive(Debug, Clone, Serialize, Deserialize)]
struct SimulationResults {
    /// Schema file fingerprint for reproducibility tracking.
    schema_hash: String,
    /// Schema file path (relative to project root).
    schema_file: String,
    /// Number of tables in the schema.
    schema_tables: usize,
    total_queries: usize,
    passed: usize,
    failed: usize,
    panicked: usize,
    /// BTreeMap for deterministic JSON key ordering across runs.
    categories: BTreeMap<String, CategoryResult>,
    failures: Vec<FailureRecord>,
    recommendations_generated: usize,
    total_bytes_scanned: u64,
    total_cost_usd: f64,
    /// BTreeMap for deterministic JSON key ordering across runs.
    tier_distribution: BTreeMap<String, usize>,
    duration_seconds: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct CategoryResult {
    total: usize,
    passed: usize,
    failed: usize,
    panicked: usize,
    avg_bytes: f64,
    avg_cost_usd: f64,
    max_cost_usd: f64,
    recommendations: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct FailureRecord {
    category: String,
    description: String,
    error: String,
}

// ============================================================================
// Query generators: each returns Vec<(description, Vec<PlanStep>)>
// ============================================================================

/// Category 1: Basic SELECT - column combinations, aliases, expressions
fn gen_basic_selects(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    for meta in metas {
        let n_cols = meta.columns.len();
        // Single column scans
        for (ci, &col) in meta.columns.iter().enumerate() {
            queries.push((
                format!("SELECT {} FROM {}", col, meta.name),
                vec![scan(1, meta.name, &[col], &[])],
            ));
            if queries.len() >= limit {
                return queries;
            }

            // Two-column combinations
            for &col2 in &meta.columns[ci + 1..] {
                queries.push((
                    format!("SELECT {}, {} FROM {}", col, col2, meta.name),
                    vec![scan(1, meta.name, &[col, col2], &[])],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // Three-column combos
        for i in 0..n_cols {
            for j in i + 1..n_cols {
                for k in j + 1..n_cols {
                    queries.push((
                        format!(
                            "SELECT {}, {}, {} FROM {}",
                            meta.columns[i], meta.columns[j], meta.columns[k], meta.name
                        ),
                        vec![scan(
                            1,
                            meta.name,
                            &[meta.columns[i], meta.columns[j], meta.columns[k]],
                            &[],
                        )],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }
        }

        // All columns (SELECT *)
        queries.push((
            format!("SELECT * FROM {}", meta.name),
            vec![scan(
                1,
                meta.name,
                &meta.columns.iter().copied().collect::<Vec<_>>(),
                &[],
            )],
        ));

        // With LIMIT / SORT
        for &col in &meta.columns {
            queries.push((
                format!(
                    "SELECT {} FROM {} ORDER BY {} LIMIT 100",
                    col, meta.name, col
                ),
                vec![
                    scan(1, meta.name, &[col], &[]),
                    sort_step(2, &[col]),
                    limit_step(3, 100),
                ],
            ));
            if queries.len() >= limit {
                return queries;
            }
        }
    }

    queries
}

/// Category 2: Filters - equality, range, BETWEEN, IN, IS NULL, AND/OR combos
fn gen_filters(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    for meta in metas {
        for &(col, ref values) in &meta.filter_columns {
            // Equality filters
            for &val in values {
                queries.push((
                    format!("SELECT * FROM {} WHERE {} = {}", meta.name, col, val),
                    vec![scan(
                        1,
                        meta.name,
                        &meta.columns.iter().copied().collect::<Vec<_>>(),
                        &[&format!("{} = {}", col, val)],
                    )],
                ));
                if queries.len() >= limit {
                    return queries;
                }

                // != filter
                queries.push((
                    format!("SELECT * FROM {} WHERE {} != {}", meta.name, col, val),
                    vec![scan(
                        1,
                        meta.name,
                        &meta.columns.iter().copied().collect::<Vec<_>>(),
                        &[&format!("{} != {}", col, val)],
                    )],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }

            // Range filters (> and <)
            for &val in values {
                queries.push((
                    format!("SELECT * FROM {} WHERE {} > {}", meta.name, col, val),
                    vec![scan(
                        1,
                        meta.name,
                        &meta.columns.iter().copied().collect::<Vec<_>>(),
                        &[&format!("{} > {}", col, val)],
                    )],
                ));
                queries.push((
                    format!("SELECT * FROM {} WHERE {} < {}", meta.name, col, val),
                    vec![scan(
                        1,
                        meta.name,
                        &meta.columns.iter().copied().collect::<Vec<_>>(),
                        &[&format!("{} < {}", col, val)],
                    )],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }

            // BETWEEN filters (pairs of values)
            for i in 0..values.len() {
                for j in i + 1..values.len() {
                    queries.push((
                        format!(
                            "SELECT * FROM {} WHERE {} BETWEEN {} AND {}",
                            meta.name, col, values[i], values[j]
                        ),
                        vec![scan(
                            1,
                            meta.name,
                            &meta.columns.iter().copied().collect::<Vec<_>>(),
                            &[&format!("{} BETWEEN {} AND {}", col, values[i], values[j])],
                        )],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }

            // IN list filters (varying sizes)
            for size in 1..=values.len().min(5) {
                let in_vals = values[..size].join(", ");
                queries.push((
                    format!("SELECT * FROM {} WHERE {} IN ({})", meta.name, col, in_vals),
                    vec![scan(
                        1,
                        meta.name,
                        &meta.columns.iter().copied().collect::<Vec<_>>(),
                        &[&format!("{} IN ({})", col, in_vals)],
                    )],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }

            // IS NULL / IS NOT NULL
            queries.push((
                format!("SELECT * FROM {} WHERE {} IS NULL", meta.name, col),
                vec![scan(
                    1,
                    meta.name,
                    &meta.columns.iter().copied().collect::<Vec<_>>(),
                    &[&format!("{} IS NULL", col)],
                )],
            ));
            queries.push((
                format!("SELECT * FROM {} WHERE {} IS NOT NULL", meta.name, col),
                vec![scan(
                    1,
                    meta.name,
                    &meta.columns.iter().copied().collect::<Vec<_>>(),
                    &[&format!("{} IS NOT NULL", col)],
                )],
            ));
            if queries.len() >= limit {
                return queries;
            }

            // LIKE filter
            queries.push((
                format!("SELECT * FROM {} WHERE {} LIKE 'abc%'", meta.name, col),
                vec![scan(
                    1,
                    meta.name,
                    &meta.columns.iter().copied().collect::<Vec<_>>(),
                    &[&format!("{} LIKE 'abc%'", col)],
                )],
            ));
            if queries.len() >= limit {
                return queries;
            }
        }

        // AND combinations: pairs of filter columns
        let fc = &meta.filter_columns;
        for i in 0..fc.len() {
            for j in i + 1..fc.len() {
                let (col1, vals1) = &fc[i];
                let (col2, vals2) = &fc[j];
                for &v1 in vals1 {
                    for &v2 in vals2 {
                        queries.push((
                            format!(
                                "SELECT * FROM {} WHERE {} = {} AND {} = {}",
                                meta.name, col1, v1, col2, v2
                            ),
                            vec![scan(
                                1,
                                meta.name,
                                &meta.columns.iter().copied().collect::<Vec<_>>(),
                                &[&format!("{} = {}", col1, v1), &format!("{} = {}", col2, v2)],
                            )],
                        ));
                        if queries.len() >= limit {
                            return queries;
                        }
                    }
                }
            }
        }

        // Triple AND combinations: 3 filter columns
        for i in 0..fc.len() {
            for j in i + 1..fc.len() {
                for k in j + 1..fc.len() {
                    let (col1, vals1) = &fc[i];
                    let (col2, vals2) = &fc[j];
                    let (col3, vals3) = &fc[k];
                    // Use first 3 values from each to control combinatorial explosion
                    for &v1 in &vals1[..vals1.len().min(3)] {
                        for &v2 in &vals2[..vals2.len().min(3)] {
                            for &v3 in &vals3[..vals3.len().min(3)] {
                                queries.push((
                                    format!(
                                        "SELECT * FROM {} WHERE {} = {} AND {} = {} AND {} = {}",
                                        meta.name, col1, v1, col2, v2, col3, v3
                                    ),
                                    vec![scan(
                                        1,
                                        meta.name,
                                        &meta.columns.iter().copied().collect::<Vec<_>>(),
                                        &[
                                            &format!("{} = {}", col1, v1),
                                            &format!("{} = {}", col2, v2),
                                            &format!("{} = {}", col3, v3),
                                        ],
                                    )],
                                ));
                                if queries.len() >= limit {
                                    return queries;
                                }
                            }
                        }
                    }
                }
            }
        }

        // Range AND equality combos
        for &(col1, ref vals1) in fc {
            for &(col2, ref vals2) in fc {
                if col1 != col2 {
                    for &v1 in &vals1[..vals1.len().min(3)] {
                        for &v2 in &vals2[..vals2.len().min(3)] {
                            queries.push((
                                format!(
                                    "SELECT * FROM {} WHERE {} > {} AND {} = {}",
                                    meta.name, col1, v1, col2, v2
                                ),
                                vec![scan(
                                    1,
                                    meta.name,
                                    &meta.columns[..3.min(meta.columns.len())],
                                    &[&format!("{} > {}", col1, v1), &format!("{} = {}", col2, v2)],
                                )],
                            ));
                            if queries.len() >= limit {
                                return queries;
                            }
                        }
                    }
                }
            }
        }
    }

    queries
}

/// Category 3: JOINs - INNER, LEFT, RIGHT, FULL, CROSS, multi-table
fn gen_joins(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();
    let pairs = join_pairs();

    let join_kinds = [
        JoinKind::Inner,
        JoinKind::Left,
        JoinKind::Right,
        JoinKind::Full,
        JoinKind::Cross,
    ];

    let meta_map: HashMap<&str, &TableMeta> = metas.iter().map(|m| (m.name, m)).collect();

    // Two-table joins: every pair x every join kind
    for &(left, right, cond) in &pairs {
        let left_meta = meta_map[left];
        let right_meta = meta_map[right];

        for &kind in &join_kinds {
            let join_cond = if kind == JoinKind::Cross { "" } else { cond };

            // Basic join
            queries.push((
                format!("{} {} JOIN {}", left, kind, right),
                vec![
                    scan(
                        1,
                        left,
                        &left_meta.columns[..3.min(left_meta.columns.len())]
                            .iter()
                            .copied()
                            .collect::<Vec<_>>(),
                        &[],
                    ),
                    scan(
                        2,
                        right,
                        &right_meta.columns[..3.min(right_meta.columns.len())]
                            .iter()
                            .copied()
                            .collect::<Vec<_>>(),
                        &[],
                    ),
                    join(3, kind, left, right, join_cond),
                ],
            ));
            if queries.len() >= limit {
                return queries;
            }

            // Join with filter on left
            for &(col, ref vals) in &left_meta.filter_columns {
                for &val in vals {
                    queries.push((
                        format!(
                            "{} {} JOIN {} WHERE {}.{} = {}",
                            left, kind, right, left, col, val
                        ),
                        vec![
                            scan(
                                1,
                                left,
                                &left_meta.columns[..3.min(left_meta.columns.len())]
                                    .iter()
                                    .copied()
                                    .collect::<Vec<_>>(),
                                &[&format!("{} = {}", col, val)],
                            ),
                            scan(
                                2,
                                right,
                                &right_meta.columns[..2.min(right_meta.columns.len())]
                                    .iter()
                                    .copied()
                                    .collect::<Vec<_>>(),
                                &[],
                            ),
                            join(3, kind, left, right, join_cond),
                        ],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }
        }
    }

    // Three-table joins
    let triple_combos = vec![
        ("fact_orders", "dim_customers", "dim_products"),
        ("fact_orders", "dim_customers", "dim_regions"),
        ("fact_orders", "dim_products", "dim_regions"),
        ("fact_pageviews", "dim_customers", "dim_regions"),
    ];

    for &(t1, t2, t3) in &triple_combos {
        let m1 = meta_map[t1];
        let m2 = meta_map[t2];
        let m3 = meta_map[t3];

        for &k1 in &join_kinds[..4] {
            for &k2 in &join_kinds[..4] {
                queries.push((
                    format!("{} {} JOIN {} {} JOIN {}", t1, k1, t2, k2, t3),
                    vec![
                        scan(1, t1, &m1.columns[..2], &[]),
                        scan(2, t2, &m2.columns[..2], &[]),
                        scan(3, t3, &m3.columns[..2], &[]),
                        join(4, k1, t1, t2, &format!("{}.id = {}.id", t1, t2)),
                        join(5, k2, "result", t3, &format!("{}.id = {}.id", t1, t3)),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    // Self-joins
    for meta in metas {
        queries.push((
            format!("{} SELF JOIN", meta.name),
            vec![
                scan(1, meta.name, &meta.columns[..2], &[]),
                scan(2, meta.name, &meta.columns[..2], &[]),
                join(
                    3,
                    JoinKind::Inner,
                    meta.name,
                    meta.name,
                    &format!("{0}.id = {0}.id", meta.name),
                ),
            ],
        ));
        if queries.len() >= limit {
            return queries;
        }
    }

    queries
}

/// Category 4: Aggregations - COUNT/SUM/AVG/MIN/MAX, GROUP BY 1-3 cols, HAVING
fn gen_aggregations(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();
    let agg_fns = [
        "COUNT(*)",
        "SUM(amount)",
        "AVG(amount)",
        "MIN(id)",
        "MAX(id)",
    ];

    for meta in metas {
        // Simple aggregation without GROUP BY
        for &func in &agg_fns {
            queries.push((
                format!("SELECT {} FROM {}", func, meta.name),
                vec![
                    scan(
                        1,
                        meta.name,
                        &meta.columns[..2.min(meta.columns.len())],
                        &[],
                    ),
                    aggregate(2, &[], &[func]),
                ],
            ));
            if queries.len() >= limit {
                return queries;
            }
        }

        // GROUP BY 1 column
        for &col in &meta.columns {
            for &func in &agg_fns {
                queries.push((
                    format!(
                        "SELECT {}, {} FROM {} GROUP BY {}",
                        col, func, meta.name, col
                    ),
                    vec![
                        scan(1, meta.name, &[col], &[]),
                        aggregate(2, &[col], &[func]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // GROUP BY 2 columns
        let n = meta.columns.len();
        for i in 0..n {
            for j in i + 1..n {
                for &func in &agg_fns {
                    queries.push((
                        format!(
                            "SELECT {}, {}, {} FROM {} GROUP BY {}, {}",
                            meta.columns[i],
                            meta.columns[j],
                            func,
                            meta.name,
                            meta.columns[i],
                            meta.columns[j]
                        ),
                        vec![
                            scan(1, meta.name, &[meta.columns[i], meta.columns[j]], &[]),
                            aggregate(2, &[meta.columns[i], meta.columns[j]], &[func]),
                        ],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }
        }

        // GROUP BY 3 columns
        for i in 0..n {
            for j in i + 1..n {
                for k in j + 1..n {
                    queries.push((
                        format!(
                            "SELECT {},{},{},COUNT(*) FROM {} GROUP BY {},{},{}",
                            meta.columns[i],
                            meta.columns[j],
                            meta.columns[k],
                            meta.name,
                            meta.columns[i],
                            meta.columns[j],
                            meta.columns[k]
                        ),
                        vec![
                            scan(
                                1,
                                meta.name,
                                &[meta.columns[i], meta.columns[j], meta.columns[k]],
                                &[],
                            ),
                            aggregate(
                                2,
                                &[meta.columns[i], meta.columns[j], meta.columns[k]],
                                &["COUNT(*)"],
                            ),
                        ],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }
        }

        // With filters + aggregation
        for &(col, ref vals) in &meta.filter_columns {
            for &val in vals {
                queries.push((
                    format!("SELECT COUNT(*) FROM {} WHERE {} = {}", meta.name, col, val),
                    vec![
                        scan(
                            1,
                            meta.name,
                            &meta.columns[..2.min(meta.columns.len())],
                            &[&format!("{} = {}", col, val)],
                        ),
                        aggregate(2, &[], &["COUNT(*)"]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    queries
}

/// Category 5: Window functions
fn gen_windows(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();
    let win_fns = [
        "ROW_NUMBER()",
        "RANK()",
        "DENSE_RANK()",
        "LAG(id)",
        "LEAD(id)",
        "SUM(id)",
        "AVG(id)",
        "COUNT(*)",
    ];

    for meta in metas {
        let n = meta.columns.len();

        // Single window function, partition by each column
        for &func in &win_fns {
            for &pcol in &meta.columns {
                for &ocol in &meta.columns {
                    if pcol != ocol {
                        queries.push((
                            format!(
                                "{} OVER (PARTITION BY {} ORDER BY {}) FROM {}",
                                func, pcol, ocol, meta.name
                            ),
                            vec![
                                scan(1, meta.name, &meta.columns[..3.min(n)], &[]),
                                window(2, func, &[pcol], &[ocol]),
                            ],
                        ));
                        if queries.len() >= limit {
                            return queries;
                        }
                    }
                }
            }
        }

        // Multiple partition columns
        for i in 0..n {
            for j in i + 1..n {
                for &func in &win_fns[..4] {
                    queries.push((
                        format!(
                            "{} OVER (PARTITION BY {},{}) FROM {}",
                            func, meta.columns[i], meta.columns[j], meta.name
                        ),
                        vec![
                            scan(1, meta.name, &meta.columns[..3.min(n)], &[]),
                            window(
                                2,
                                func,
                                &[meta.columns[i], meta.columns[j]],
                                &[meta.columns[0]],
                            ),
                        ],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }
        }

        // Window + filter
        for &(fcol, ref vals) in &meta.filter_columns {
            for &val in vals {
                queries.push((
                    format!(
                        "ROW_NUMBER() OVER(...) FROM {} WHERE {} = {}",
                        meta.name, fcol, val
                    ),
                    vec![
                        scan(
                            1,
                            meta.name,
                            &meta.columns[..3.min(n)],
                            &[&format!("{} = {}", fcol, val)],
                        ),
                        window(
                            2,
                            "ROW_NUMBER()",
                            &[meta.columns[0]],
                            &[meta.columns[1.min(n - 1)]],
                        ),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    queries
}

/// Category 6: Subqueries - IN, EXISTS, scalar, correlated
fn gen_subqueries(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    for meta in metas {
        // IN subquery with each filter value
        for &(col, ref vals) in &meta.filter_columns {
            for &val in vals {
                queries.push((
                    format!(
                        "SELECT * FROM {} WHERE {} IN (SELECT {} FROM {} WHERE {} = {})",
                        meta.name, col, col, meta.name, col, val
                    ),
                    vec![
                        scan(
                            1,
                            meta.name,
                            &meta.columns.iter().copied().collect::<Vec<_>>(),
                            &[],
                        ),
                        subquery(
                            2,
                            "subq",
                            vec![scan(1, meta.name, &[col], &[&format!("{} = {}", col, val)])],
                        ),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // Correlated subquery: outer + inner on same table
        for &(col, ref vals) in &meta.filter_columns {
            for &val in vals {
                queries.push((
                    format!("correlated subquery on {}.{} = {}", meta.name, col, val),
                    vec![
                        scan(
                            1,
                            meta.name,
                            &meta.columns[..3.min(meta.columns.len())],
                            &[],
                        ),
                        subquery(
                            2,
                            "corr",
                            vec![scan(
                                1,
                                meta.name,
                                &[col],
                                &[
                                    &format!("{} = outer.{}", col, col),
                                    &format!("{} = {}", col, val),
                                ],
                            )],
                        ),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // Derived table from another table
        for other in metas {
            if other.name != meta.name {
                queries.push((
                    format!(
                        "SELECT * FROM {} WHERE id IN (SELECT id FROM {})",
                        meta.name, other.name
                    ),
                    vec![
                        scan(1, meta.name, &meta.columns[..2], &[]),
                        subquery(2, "derived", vec![scan(1, other.name, &["id"], &[])]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // Multi-level nesting
        for other in metas {
            if other.name != meta.name {
                queries.push((
                    format!("nested subquery {} -> {}", meta.name, other.name),
                    vec![
                        scan(1, meta.name, &meta.columns[..2], &[]),
                        subquery(
                            2,
                            "level1",
                            vec![
                                scan(1, other.name, &other.columns[..2], &[]),
                                subquery(2, "level2", vec![scan(1, meta.name, &["id"], &[])]),
                            ],
                        ),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    queries
}

/// Category 7: CTEs
fn gen_ctes(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    for meta in metas {
        // Simple CTE = just a scan
        for &(col, ref vals) in &meta.filter_columns {
            for &val in vals {
                queries.push((
                    format!("CTE on {} WHERE {} = {}", meta.name, col, val),
                    vec![
                        scan(
                            1,
                            meta.name,
                            &meta.columns[..3.min(meta.columns.len())],
                            &[&format!("{} = {}", col, val)],
                        ),
                        aggregate(2, &[col], &["COUNT(*)"]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // CTE + join with another table
        for other in metas {
            if other.name != meta.name {
                queries.push((
                    format!("CTE {} + JOIN {}", meta.name, other.name),
                    vec![
                        scan(1, meta.name, &meta.columns[..2], &[]),
                        scan(2, other.name, &other.columns[..2], &[]),
                        join(
                            3,
                            JoinKind::Inner,
                            meta.name,
                            other.name,
                            &format!("{}.id = {}.id", meta.name, other.name),
                        ),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // Multi-CTE: 2 scans + aggregate
        for &(col, ref vals) in &meta.filter_columns {
            for &val in vals {
                queries.push((
                    format!("multi-CTE {} {} = {}", meta.name, col, val),
                    vec![
                        scan(
                            1,
                            meta.name,
                            &meta.columns[..3.min(meta.columns.len())],
                            &[&format!("{} = {}", col, val)],
                        ),
                        scan(2, meta.name, &[col], &[]),
                        aggregate(3, &[col], &["SUM(id)", "COUNT(*)"]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    queries
}

/// Category 8: Set operations - UNION, UNION ALL, INTERSECT, EXCEPT
fn gen_set_operations(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    for meta in metas {
        // UNION ALL / UNION of same table with different filters
        let fc = &meta.filter_columns;
        for &(col, ref vals) in fc {
            for i in 0..vals.len() {
                for j in i + 1..vals.len() {
                    // UNION ALL
                    queries.push((
                        format!(
                            "UNION ALL {} WHERE {}={} / {}={}",
                            meta.name, col, vals[i], col, vals[j]
                        ),
                        vec![
                            scan(
                                1,
                                meta.name,
                                &meta.columns[..2.min(meta.columns.len())],
                                &[&format!("{} = {}", col, vals[i])],
                            ),
                            scan(
                                2,
                                meta.name,
                                &meta.columns[..2.min(meta.columns.len())],
                                &[&format!("{} = {}", col, vals[j])],
                            ),
                            union_op(3, true, 2),
                        ],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }

                    // UNION (distinct)
                    queries.push((
                        format!(
                            "UNION {} WHERE {}={} / {}={}",
                            meta.name, col, vals[i], col, vals[j]
                        ),
                        vec![
                            scan(
                                1,
                                meta.name,
                                &meta.columns[..2.min(meta.columns.len())],
                                &[&format!("{} = {}", col, vals[i])],
                            ),
                            scan(
                                2,
                                meta.name,
                                &meta.columns[..2.min(meta.columns.len())],
                                &[&format!("{} = {}", col, vals[j])],
                            ),
                            union_op(3, false, 2),
                        ],
                    ));
                    if queries.len() >= limit {
                        return queries;
                    }
                }
            }
        }

        // UNION across different tables
        for other in metas {
            if other.name != meta.name {
                queries.push((
                    format!("UNION ALL {} + {}", meta.name, other.name),
                    vec![
                        scan(1, meta.name, &["id"], &[]),
                        scan(2, other.name, &["id"], &[]),
                        union_op(3, true, 2),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }

        // Three-way union
        for i in 0..metas.len() {
            for j in i + 1..metas.len() {
                queries.push((
                    format!(
                        "3-way UNION ALL {} + {} + {}",
                        meta.name, metas[i].name, metas[j].name
                    ),
                    vec![
                        scan(1, meta.name, &["id"], &[]),
                        scan(2, metas[i].name, &["id"], &[]),
                        scan(3, metas[j].name, &["id"], &[]),
                        union_op(4, true, 3),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    queries
}

/// Category 9: Complex combinations - joins + subqueries + windows
fn gen_complex(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();
    let meta_map: HashMap<&str, &TableMeta> = metas.iter().map(|m| (m.name, m)).collect();

    // Join + window
    let pairs = join_pairs();
    for &(left, right, cond) in &pairs {
        let lm = meta_map[left];
        let rm = meta_map[right];

        for &func in &["ROW_NUMBER()", "RANK()", "SUM(id)", "AVG(id)"] {
            queries.push((
                format!("{} JOIN {} + {} window", left, right, func),
                vec![
                    scan(1, left, &lm.columns[..3.min(lm.columns.len())], &[]),
                    scan(2, right, &rm.columns[..2.min(rm.columns.len())], &[]),
                    join(3, JoinKind::Inner, left, right, cond),
                    window(
                        4,
                        func,
                        &[lm.columns[0]],
                        &[lm.columns[1.min(lm.columns.len() - 1)]],
                    ),
                ],
            ));
            if queries.len() >= limit {
                return queries;
            }
        }
    }

    // Join + aggregation + filter
    for &(left, right, cond) in &pairs {
        let lm = meta_map[left];
        let rm = meta_map[right];

        for &(col, ref vals) in &lm.filter_columns {
            for &val in vals {
                queries.push((
                    format!("{} JOIN {} WHERE {} = {} GROUP BY", left, right, col, val),
                    vec![
                        scan(
                            1,
                            left,
                            &lm.columns[..3.min(lm.columns.len())],
                            &[&format!("{} = {}", col, val)],
                        ),
                        scan(2, right, &rm.columns[..2.min(rm.columns.len())], &[]),
                        join(3, JoinKind::Inner, left, right, cond),
                        aggregate(4, &[col], &["COUNT(*)", "SUM(id)"]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    // Join + subquery
    for &(left, right, cond) in &pairs {
        let lm = meta_map[left];
        let rm = meta_map[right];

        queries.push((
            format!("{} JOIN {} + subquery on {}", left, right, right),
            vec![
                scan(1, left, &lm.columns[..2], &[]),
                scan(2, right, &rm.columns[..2], &[]),
                join(3, JoinKind::Inner, left, right, cond),
                subquery(4, "sub", vec![scan(1, right, &["id"], &[])]),
            ],
        ));
        if queries.len() >= limit {
            return queries;
        }
    }

    // Multi-join + filter + window + aggregate (the kitchen sink)
    let triple = vec![
        ("fact_orders", "dim_customers", "dim_products"),
        ("fact_orders", "dim_customers", "dim_regions"),
    ];

    for &(t1, t2, t3) in &triple {
        let m1 = meta_map[t1];
        let m2 = meta_map[t2];
        let m3 = meta_map[t3];

        for &(col, ref vals) in &m1.filter_columns {
            for &val in vals {
                queries.push((
                    format!(
                        "{} JOIN {} JOIN {} WHERE {}.{} = {} + window + agg",
                        t1, t2, t3, t1, col, val
                    ),
                    vec![
                        scan(
                            1,
                            t1,
                            &m1.columns[..3.min(m1.columns.len())],
                            &[&format!("{} = {}", col, val)],
                        ),
                        scan(2, t2, &m2.columns[..2], &[]),
                        scan(3, t3, &m3.columns[..2], &[]),
                        join(
                            4,
                            JoinKind::Inner,
                            t1,
                            t2,
                            &format!("{}.id = {}.id", t1, t2),
                        ),
                        join(
                            5,
                            JoinKind::Left,
                            "result",
                            t3,
                            &format!("{}.id = {}.id", t1, t3),
                        ),
                        window(6, "ROW_NUMBER()", &[col], &[m1.columns[0]]),
                        aggregate(7, &[col], &["COUNT(*)"]),
                    ],
                ));
                if queries.len() >= limit {
                    return queries;
                }
            }
        }
    }

    // Deep nesting: 4-table join
    queries.push((
        "4-table join: orders-customers-products-regions".to_string(),
        vec![
            scan(
                1,
                "fact_orders",
                &["id", "user_id", "product_id", "region"],
                &[],
            ),
            scan(2, "dim_customers", &["id", "name"], &[]),
            scan(3, "dim_products", &["id", "name"], &[]),
            scan(4, "dim_regions", &["id", "name"], &[]),
            join(
                5,
                JoinKind::Inner,
                "fact_orders",
                "dim_customers",
                "fact_orders.user_id = dim_customers.id",
            ),
            join(
                6,
                JoinKind::Inner,
                "result",
                "dim_products",
                "fact_orders.product_id = dim_products.id",
            ),
            join(
                7,
                JoinKind::Left,
                "result",
                "dim_regions",
                "fact_orders.region = dim_regions.name",
            ),
        ],
    ));

    // 5-table join
    queries.push((
        "5-table join: orders-customers-products-regions-pageviews".to_string(),
        vec![
            scan(
                1,
                "fact_orders",
                &["id", "user_id", "product_id", "region"],
                &[],
            ),
            scan(2, "dim_customers", &["id", "name"], &[]),
            scan(3, "dim_products", &["id", "name"], &[]),
            scan(4, "dim_regions", &["id", "name"], &[]),
            scan(5, "fact_pageviews", &["user_id", "page_url"], &[]),
            join(
                6,
                JoinKind::Inner,
                "fact_orders",
                "dim_customers",
                "fact_orders.user_id = dim_customers.id",
            ),
            join(
                7,
                JoinKind::Inner,
                "result",
                "dim_products",
                "fact_orders.product_id = dim_products.id",
            ),
            join(
                8,
                JoinKind::Left,
                "result",
                "dim_regions",
                "fact_orders.region = dim_regions.name",
            ),
            join(
                9,
                JoinKind::Inner,
                "result",
                "fact_pageviews",
                "fact_orders.user_id = fact_pageviews.user_id",
            ),
        ],
    ));

    queries
}

/// Category 10: Edge cases - empty scans, metadata-only, deeply nested
fn gen_edge_cases(metas: &[TableMeta], limit: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    // Empty column list (metadata-only / COUNT(*))
    for meta in metas {
        queries.push((
            format!("COUNT(*) FROM {} (metadata-only)", meta.name),
            vec![
                scan(1, meta.name, &[], &[]),
                aggregate(2, &[], &["COUNT(*)"]),
            ],
        ));
        if queries.len() >= limit {
            return queries;
        }
    }

    // Empty plan
    queries.push(("empty plan (no steps)".to_string(), vec![]));

    // Unknown table
    queries.push((
        "unknown table scan".to_string(),
        vec![scan(1, "nonexistent_table", &["id", "name"], &[])],
    ));
    queries.push((
        "unknown table with filter".to_string(),
        vec![scan(1, "nonexistent_table", &["id"], &["id = 1"])],
    ));

    // Very wide scans (all columns from every table)
    for meta in metas {
        queries.push((
            format!("all columns from {}", meta.name),
            vec![scan(
                1,
                meta.name,
                &meta.columns.iter().copied().collect::<Vec<_>>(),
                &[],
            )],
        ));
        if queries.len() >= limit {
            return queries;
        }
    }

    // Many filters on same table
    for meta in metas {
        let mut all_filters = Vec::new();
        for &(col, ref vals) in &meta.filter_columns {
            if let Some(&val) = vals.first() {
                all_filters.push(format!("{} = {}", col, val));
            }
        }
        let filter_strs: Vec<&str> = all_filters.iter().map(|s| s.as_str()).collect();
        queries.push((
            format!("many filters on {}", meta.name),
            vec![scan(
                1,
                meta.name,
                &meta.columns[..3.min(meta.columns.len())],
                &filter_strs,
            )],
        ));
        if queries.len() >= limit {
            return queries;
        }
    }

    // Cross join edge cases (small x small, large x large)
    queries.push((
        "cross join dim_regions x dim_regions".to_string(),
        vec![
            scan(1, "dim_regions", &["id", "name"], &[]),
            scan(2, "dim_regions", &["id", "country"], &[]),
            join(3, JoinKind::Cross, "dim_regions", "dim_regions", ""),
        ],
    ));
    queries.push((
        "cross join fact_orders x dim_customers".to_string(),
        vec![
            scan(1, "fact_orders", &["id"], &[]),
            scan(2, "dim_customers", &["id"], &[]),
            join(3, JoinKind::Cross, "fact_orders", "dim_customers", ""),
        ],
    ));

    // Deeply nested subqueries (3 levels)
    queries.push((
        "3-level nested subquery".to_string(),
        vec![
            scan(1, "fact_orders", &["id", "user_id"], &[]),
            subquery(
                2,
                "l1",
                vec![
                    scan(1, "dim_customers", &["id"], &[]),
                    subquery(
                        2,
                        "l2",
                        vec![
                            scan(1, "dim_products", &["id"], &[]),
                            subquery(2, "l3", vec![scan(1, "dim_regions", &["id"], &[])]),
                        ],
                    ),
                ],
            ),
        ],
    ));

    // Multiple windows
    for meta in metas {
        let n = meta.columns.len();
        if n >= 3 {
            queries.push((
                format!("multiple windows on {}", meta.name),
                vec![
                    scan(1, meta.name, &meta.columns[..3], &[]),
                    window(2, "ROW_NUMBER()", &[meta.columns[0]], &[meta.columns[1]]),
                    window(3, "SUM(id)", &[meta.columns[1]], &[meta.columns[2]]),
                    window(4, "RANK()", &[meta.columns[2]], &[meta.columns[0]]),
                ],
            ));
            if queries.len() >= limit {
                return queries;
            }
        }
    }

    // Distinct operation
    for meta in metas {
        queries.push((
            format!("DISTINCT on {}", meta.name),
            vec![
                scan(
                    1,
                    meta.name,
                    &meta.columns[..2.min(meta.columns.len())],
                    &[],
                ),
                PlanStep {
                    step: 2,
                    operation: PlanOperation::Distinct,
                    notes: None,
                },
            ],
        ));
        if queries.len() >= limit {
            return queries;
        }
    }

    // Empty relation
    queries.push((
        "empty relation (SELECT 1)".to_string(),
        vec![PlanStep {
            step: 1,
            operation: PlanOperation::EmptyRelation,
            notes: None,
        }],
    ));

    queries
}

/// Generate additional filter queries by varying which columns are selected.
/// This fills the gap to reach target template count.
fn gen_column_variant_filters(metas: &[TableMeta], target: usize) -> Vec<(String, Vec<PlanStep>)> {
    let mut queries = Vec::new();

    for meta in metas {
        let subsets = column_subsets(&meta.columns);
        for subset in &subsets {
            for &(col, ref values) in &meta.filter_columns {
                for &val in values {
                    // Equality with different column subsets
                    queries.push((
                        format!(
                            "SELECT {} cols FROM {} WHERE {} = {}",
                            subset.len(),
                            meta.name,
                            col,
                            val
                        ),
                        vec![scan(1, meta.name, subset, &[&format!("{} = {}", col, val)])],
                    ));
                    if queries.len() >= target {
                        return queries;
                    }

                    // Range with different column subsets
                    queries.push((
                        format!(
                            "SELECT {} cols FROM {} WHERE {} > {}",
                            subset.len(),
                            meta.name,
                            col,
                            val
                        ),
                        vec![scan(1, meta.name, subset, &[&format!("{} > {}", col, val)])],
                    ));
                    if queries.len() >= target {
                        return queries;
                    }

                    // BETWEEN with column subsets
                    if values.len() >= 2 {
                        let v2 = values[values.len() - 1];
                        queries.push((
                            format!(
                                "SELECT {} cols FROM {} WHERE {} BETWEEN {} AND {}",
                                subset.len(),
                                meta.name,
                                col,
                                val,
                                v2
                            ),
                            vec![scan(
                                1,
                                meta.name,
                                subset,
                                &[&format!("{} BETWEEN {} AND {}", col, val, v2)],
                            )],
                        ));
                        if queries.len() >= target {
                            return queries;
                        }
                    }
                }
            }
        }
    }

    queries
}

// ============================================================================
// Core simulation runner
// ============================================================================

fn run_simulation(limit_per_category: Option<usize>) -> SimulationResults {
    let schema = benchmark_schema();
    let metas = table_metas();
    let dialects = [
        SqlDialect::BigQuery,
        SqlDialect::Snowflake,
        SqlDialect::Databricks,
        SqlDialect::Athena,
        SqlDialect::Redshift,
        SqlDialect::Postgres,
    ];

    let start = Instant::now();

    // Define category limits
    let (
        basic_lim,
        filter_lim,
        join_lim,
        agg_lim,
        window_lim,
        subquery_lim,
        cte_lim,
        set_lim,
        complex_lim,
        edge_lim,
    ) = if let Some(cap) = limit_per_category {
        (cap, cap, cap, cap, cap, cap, cap, cap, cap, cap)
    } else {
        // Full simulation: target 100k+ across 6 dialects
        // Each category generates N templates, run across 6 dialects = N*6 total
        // Target ~18k+ templates = 108k+ total queries
        // Use very high limits to exhaust generator capacity
        (5000, 50000, 10000, 8000, 8000, 5000, 4000, 4000, 8000, 3000)
    };

    // Calculate how many extra "column variant" templates we need
    // to reach ~18k templates (= 108k queries across 6 dialects)
    let col_variant_lim = if limit_per_category.is_some() {
        0
    } else {
        8000
    };

    let categories: Vec<(&str, Vec<(String, Vec<PlanStep>)>)> = vec![
        ("basic_select", gen_basic_selects(&metas, basic_lim)),
        ("filters", gen_filters(&metas, filter_lim)),
        ("joins", gen_joins(&metas, join_lim)),
        ("aggregations", gen_aggregations(&metas, agg_lim)),
        ("window_functions", gen_windows(&metas, window_lim)),
        ("subqueries", gen_subqueries(&metas, subquery_lim)),
        ("ctes", gen_ctes(&metas, cte_lim)),
        ("set_operations", gen_set_operations(&metas, set_lim)),
        ("complex_combinations", gen_complex(&metas, complex_lim)),
        ("edge_cases", gen_edge_cases(&metas, edge_lim)),
        (
            "column_variants",
            gen_column_variant_filters(&metas, col_variant_lim),
        ),
    ];

    let mut results = SimulationResults {
        schema_hash: schema_sha256(),
        schema_file: "tests/fixtures/schemas/benchmark_warehouse.yaml".to_string(),
        schema_tables: schema.tables.len(),
        total_queries: 0,
        passed: 0,
        failed: 0,
        panicked: 0,
        categories: BTreeMap::new(),
        failures: Vec::new(),
        recommendations_generated: 0,
        total_bytes_scanned: 0,
        total_cost_usd: 0.0,
        tier_distribution: BTreeMap::new(),
        duration_seconds: 0.0,
    };

    for (cat_name, templates) in &categories {
        let mut cat = CategoryResult {
            total: 0,
            passed: 0,
            failed: 0,
            panicked: 0,
            avg_bytes: 0.0,
            avg_cost_usd: 0.0,
            max_cost_usd: 0.0,
            recommendations: 0,
        };
        let mut bytes_sum: f64 = 0.0;
        let mut cost_sum: f64 = 0.0;

        for (desc, steps) in templates {
            for &dialect in &dialects {
                cat.total += 1;
                results.total_queries += 1;

                let steps_clone = steps.clone();
                let schema_ref = &schema;

                // Catch panics
                let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
                    estimate_cost(&steps_clone, schema_ref, dialect)
                }));

                match outcome {
                    Ok(estimate) => {
                        // Validate the estimate
                        let mut valid = true;
                        let mut errors = Vec::new();

                        if estimate.cost_usd.is_nan() {
                            valid = false;
                            errors.push("cost_usd is NaN".to_string());
                        }
                        if estimate.cost_usd.is_infinite() {
                            valid = false;
                            errors.push("cost_usd is infinite".to_string());
                        }
                        if estimate.cost_usd < 0.0 {
                            valid = false;
                            errors.push(format!("cost_usd is negative: {}", estimate.cost_usd));
                        }
                        if (estimate.bytes_scanned as f64).is_nan() {
                            valid = false;
                            errors.push("bytes_scanned is NaN".to_string());
                        }

                        // Check CostRange ordering if present
                        if let Some(ref range) = estimate.cost_range {
                            if range.low > range.expected {
                                valid = false;
                                errors.push(format!(
                                    "CostRange: low ({}) > expected ({})",
                                    range.low, range.expected
                                ));
                            }
                            if range.expected > range.high {
                                valid = false;
                                errors.push(format!(
                                    "CostRange: expected ({}) > high ({})",
                                    range.expected, range.high
                                ));
                            }
                        }

                        // Check cost_tier is not Unknown when we have stats
                        // (only for tables that actually have statistics)
                        let all_have_stats = !estimate.table_breakdown.is_empty()
                            && estimate.table_breakdown.iter().all(|tb| tb.has_statistics);
                        if estimate.cost_tier == CostTier::Unknown
                            && all_have_stats
                            && estimate.bytes_scanned > 0
                        {
                            valid = false;
                            errors.push(format!(
                                "cost_tier is Unknown but bytes_scanned = {} (all tables have stats)",
                                estimate.bytes_scanned
                            ));
                        }

                        if valid {
                            cat.passed += 1;
                            results.passed += 1;
                            bytes_sum += estimate.bytes_scanned as f64;
                            cost_sum += estimate.cost_usd;
                            results.total_bytes_scanned = results
                                .total_bytes_scanned
                                .saturating_add(estimate.bytes_scanned);
                            results.total_cost_usd += estimate.cost_usd;

                            if estimate.cost_usd > cat.max_cost_usd {
                                cat.max_cost_usd = estimate.cost_usd;
                            }

                            if !estimate.recommendations.is_empty() {
                                cat.recommendations += estimate.recommendations.len();
                                results.recommendations_generated += estimate.recommendations.len();
                            }

                            let tier_str = format!("{:?}", estimate.cost_tier);
                            *results.tier_distribution.entry(tier_str).or_insert(0) += 1;
                        } else {
                            cat.failed += 1;
                            results.failed += 1;
                            // Only keep first 500 failures to avoid memory bloat
                            if results.failures.len() < 500 {
                                results.failures.push(FailureRecord {
                                    category: cat_name.to_string(),
                                    description: format!("[{:?}] {}", dialect, desc),
                                    error: errors.join("; "),
                                });
                            }
                        }
                    }
                    Err(panic_info) => {
                        cat.panicked += 1;
                        cat.failed += 1;
                        results.panicked += 1;
                        results.failed += 1;
                        if results.failures.len() < 500 {
                            let panic_msg = if let Some(s) = panic_info.downcast_ref::<String>() {
                                s.clone()
                            } else if let Some(s) = panic_info.downcast_ref::<&str>() {
                                s.to_string()
                            } else {
                                "unknown panic".to_string()
                            };
                            results.failures.push(FailureRecord {
                                category: cat_name.to_string(),
                                description: format!("[{:?}] {}", dialect, desc),
                                error: format!("PANIC: {}", panic_msg),
                            });
                        }
                    }
                }
            }
        }

        if cat.passed > 0 {
            cat.avg_bytes = bytes_sum / cat.passed as f64;
            cat.avg_cost_usd = cost_sum / cat.passed as f64;
        }

        results.categories.insert(cat_name.to_string(), cat);
    }

    results.duration_seconds = start.elapsed().as_secs_f64();
    results
}

// ============================================================================
// Test entry points
// ============================================================================

/// Smoke test: runs ~300 query templates x 3 dialects = ~900 queries.
/// Included in normal `cargo test` (not ignored).
#[test]
fn simulation_smoke_test() {
    let results = run_simulation(Some(100));

    println!("=== Simulation Smoke Test ===");
    println!(
        "Total: {} | Passed: {} | Failed: {} | Panicked: {}",
        results.total_queries, results.passed, results.failed, results.panicked
    );
    println!("Duration: {:.2}s", results.duration_seconds);

    for (cat, res) in &results.categories {
        println!(
            "  {}: {}/{} passed (avg ${:.6})",
            cat, res.passed, res.total, res.avg_cost_usd
        );
    }

    if !results.failures.is_empty() {
        println!("\nFirst 10 failures:");
        for f in results.failures.iter().take(10) {
            println!("  [{}] {} -> {}", f.category, f.description, f.error);
        }
    }

    // Assert no panics
    assert_eq!(
        results.panicked, 0,
        "No queries should panic, but {} did",
        results.panicked
    );

    // Assert high pass rate (>95%)
    let pass_rate = results.passed as f64 / results.total_queries as f64;
    assert!(
        pass_rate > 0.95,
        "Pass rate {:.1}% should be >95%. Failed: {}, Total: {}",
        pass_rate * 100.0,
        results.failed,
        results.total_queries
    );
}

/// Full simulation: runs 100k+ queries. Takes several minutes.
/// Run with: `cargo test --release -p altimate-core simulation_full_100k -- --ignored --nocapture`
#[test]
#[ignore]
fn simulation_full_100k() {
    let results = run_simulation(None);

    println!("\n=== Full 100k+ Simulation Results ===");
    println!("Schema hash: {}", results.schema_hash);
    println!("Schema file: {}", results.schema_file);
    println!("Schema tables: {}", results.schema_tables);
    println!(
        "Total: {} | Passed: {} | Failed: {} | Panicked: {}",
        results.total_queries, results.passed, results.failed, results.panicked
    );
    println!("Duration: {:.2}s", results.duration_seconds);
    println!(
        "Total bytes scanned: {} ({:.2} TB)",
        results.total_bytes_scanned,
        results.total_bytes_scanned as f64 / 1e12
    );
    println!("Total estimated cost: ${:.2}", results.total_cost_usd);
    println!(
        "Recommendations generated: {}",
        results.recommendations_generated
    );
    println!("\nTier distribution:");
    for (tier, count) in &results.tier_distribution {
        println!("  {}: {}", tier, count);
    }
    println!("\nCategory breakdown:");
    for (cat, res) in &results.categories {
        println!(
            "  {}: {}/{} passed, {} panicked, avg ${:.6}, max ${:.2}, {} recs",
            cat,
            res.passed,
            res.total,
            res.panicked,
            res.avg_cost_usd,
            res.max_cost_usd,
            res.recommendations
        );
    }

    if !results.failures.is_empty() {
        println!("\nFirst 20 failures:");
        for f in results.failures.iter().take(20) {
            println!("  [{}] {} -> {}", f.category, f.description, f.error);
        }
    }

    // Write results JSON (deterministic: BTreeMap keys are sorted, duration excluded from diff)
    let output_dir =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("../../experiments/snowflake_validation");
    std::fs::create_dir_all(&output_dir).ok();
    let output_path = output_dir.join("simulation_results.json");
    let json = serde_json::to_string_pretty(&results).expect("Failed to serialize results");
    std::fs::write(&output_path, &json)
        .unwrap_or_else(|e| panic!("Failed to write results to {}: {e}", output_path.display()));
    println!("\nResults written to {}", output_path.display());

    // Write a stable summary file (excludes timing for clean diffing across runs)
    let stable_summary = StableSummary {
        schema_hash: results.schema_hash.clone(),
        schema_file: results.schema_file.clone(),
        schema_tables: results.schema_tables,
        total_queries: results.total_queries,
        passed: results.passed,
        failed: results.failed,
        panicked: results.panicked,
        categories: results
            .categories
            .iter()
            .map(|(k, v)| {
                (
                    k.clone(),
                    StableCategorySummary {
                        total: v.total,
                        passed: v.passed,
                        failed: v.failed,
                        panicked: v.panicked,
                    },
                )
            })
            .collect(),
        tier_distribution: results.tier_distribution.clone(),
    };
    let stable_path = output_dir.join("simulation_stable_summary.json");
    let stable_json =
        serde_json::to_string_pretty(&stable_summary).expect("Failed to serialize stable summary");
    std::fs::write(&stable_path, &stable_json).unwrap_or_else(|e| {
        panic!(
            "Failed to write stable summary to {}: {e}",
            stable_path.display()
        )
    });
    println!("Stable summary written to {}", stable_path.display());

    // Assertions
    assert!(
        results.total_queries >= 100_000,
        "Should generate at least 100k queries, got {}",
        results.total_queries
    );
    assert_eq!(
        results.panicked, 0,
        "No queries should panic, but {} did",
        results.panicked
    );

    let pass_rate = results.passed as f64 / results.total_queries as f64;
    assert!(
        pass_rate > 0.95,
        "Pass rate {:.1}% should be >95%. Failed: {}",
        pass_rate * 100.0,
        results.failed
    );
}

/// Stable summary for diffing across runs (excludes timing, byte/cost aggregates).
#[derive(Debug, Clone, Serialize, Deserialize)]
struct StableSummary {
    schema_hash: String,
    schema_file: String,
    schema_tables: usize,
    total_queries: usize,
    passed: usize,
    failed: usize,
    panicked: usize,
    categories: BTreeMap<String, StableCategorySummary>,
    tier_distribution: BTreeMap<String, usize>,
}

/// Per-category counts only (no floating-point values that might vary).
#[derive(Debug, Clone, Serialize, Deserialize)]
struct StableCategorySummary {
    total: usize,
    passed: usize,
    failed: usize,
    panicked: usize,
}
