//! Diagnose `_HT_TO_FILTER` appearing as unresolved table alias in column_dict.
//!
//! Reads the snowflake ground truth fixture, runs `projection_column_lineage`,
//! and for each case where `_HT_TO_FILTER` appears in column_dict values,
//! prints the query name, SQL prefix, schema tables, offending column_dict
//! entries, and the CTE names / subquery aliases extracted from the AST.
//!
//! Run: cargo run --example alias_debug

use altimate_core::lineage::column_lineage;
use altimate_core::schema::types::SqlDialect;
use datafusion::sql::sqlparser::ast::{self, SetExpr, Statement};
use datafusion::sql::sqlparser::dialect::SnowflakeDialect;
use datafusion::sql::sqlparser::parser::Parser;
use serde::Deserialize;
use std::collections::HashMap;
use std::io::BufRead;
use std::path::Path;

#[derive(Deserialize)]
struct Case {
    name: String,
    sql: String,
    schema: serde_json::Value,
    #[allow(dead_code)]
    expected: HashMap<String, Vec<String>>,
}

fn schema_from_json(value: &serde_json::Value) -> HashMap<String, serde_json::Value> {
    match value {
        serde_json::Value::Object(map) => map.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
        _ => HashMap::new(),
    }
}

/// Extract CTE names from parsed statements.
fn extract_cte_names(stmts: &[Statement]) -> Vec<String> {
    let mut names = Vec::new();
    for stmt in stmts {
        let query = match stmt {
            Statement::Query(q) => q.as_ref(),
            Statement::Insert(ins) => {
                if let Some(ref src) = ins.source {
                    src.as_ref()
                } else {
                    continue;
                }
            }
            Statement::CreateTable(ct) => {
                if let Some(ref q) = ct.query {
                    q.as_ref()
                } else {
                    continue;
                }
            }
            _ => continue,
        };
        if let Some(with) = &query.with {
            for cte in &with.cte_tables {
                names.push(cte.alias.name.value.clone());
            }
        }
    }
    names
}

/// Recursively collect subquery aliases from FROM clauses.
fn collect_subquery_aliases(stmts: &[Statement]) -> Vec<String> {
    let mut aliases = Vec::new();
    for stmt in stmts {
        let query = match stmt {
            Statement::Query(q) => q.as_ref(),
            Statement::Insert(ins) => {
                if let Some(ref src) = ins.source {
                    src.as_ref()
                } else {
                    continue;
                }
            }
            Statement::CreateTable(ct) => {
                if let Some(ref q) = ct.query {
                    q.as_ref()
                } else {
                    continue;
                }
            }
            _ => continue,
        };
        collect_subquery_aliases_from_body(&query.body, &mut aliases);
    }
    aliases
}

fn collect_subquery_aliases_from_body(body: &SetExpr, out: &mut Vec<String>) {
    match body {
        SetExpr::Select(sel) => {
            for twj in &sel.from {
                collect_subquery_aliases_from_factor(&twj.relation, out);
                for join in &twj.joins {
                    collect_subquery_aliases_from_factor(&join.relation, out);
                }
            }
        }
        SetExpr::SetOperation { left, right, .. } => {
            collect_subquery_aliases_from_body(left, out);
            collect_subquery_aliases_from_body(right, out);
        }
        SetExpr::Query(q) => {
            collect_subquery_aliases_from_body(&q.body, out);
        }
        _ => {}
    }
}

fn collect_subquery_aliases_from_factor(factor: &ast::TableFactor, out: &mut Vec<String>) {
    match factor {
        ast::TableFactor::Derived {
            alias, subquery, ..
        } => {
            if let Some(a) = alias {
                out.push(a.name.value.clone());
            }
            // Recurse into the subquery body for nested subqueries
            collect_subquery_aliases_from_body(&subquery.body, out);
        }
        ast::TableFactor::NestedJoin {
            table_with_joins, ..
        } => {
            collect_subquery_aliases_from_factor(&table_with_joins.relation, out);
            for join in &table_with_joins.joins {
                collect_subquery_aliases_from_factor(&join.relation, out);
            }
        }
        _ => {}
    }
}

fn main() {
    let fixture_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../tests/fixtures/lineage/snowflake_ground_truth.jsonl");
    let file = std::fs::File::open(&fixture_path).expect("open fixture");
    let reader = std::io::BufReader::with_capacity(1024 * 1024, file);
    let dialect = SqlDialect::Snowflake;
    let parser_dialect = SnowflakeDialect {};

    let mut total = 0u64;
    let mut ht_queries = 0u64;
    let mut ht_refs = 0u64;
    let mut detailed = 0u64;

    // Track unique _HT_ alias variants (case-insensitive key → original forms)
    let mut ht_alias_variants: HashMap<String, u64> = HashMap::new();

    for line in reader.lines() {
        let line = line.expect("read line");
        let line = line.trim();
        if line.is_empty() {
            continue;
        }
        let case: Case = match serde_json::from_str(line) {
            Ok(c) => c,
            Err(_) => continue,
        };
        total += 1;

        let schema = schema_from_json(&case.schema);
        let result = match column_lineage(&case.sql, dialect, &schema, None) {
            Ok(r) => r,
            Err(_) => continue,
        };

        // Find column_dict entries referencing _HT_TO_FILTER (case-insensitive)
        let mut offending: Vec<(String, Vec<String>)> = Vec::new();
        for (col, refs) in &result.column_dict {
            let ht_refs_found: Vec<String> = refs
                .iter()
                .filter(|r| r.to_uppercase().contains("_HT_TO_FILTER"))
                .cloned()
                .collect();
            if !ht_refs_found.is_empty() {
                offending.push((col.clone(), ht_refs_found));
            }
        }

        if offending.is_empty() {
            continue;
        }

        ht_queries += 1;
        let this_ref_count: u64 = offending.iter().map(|(_, v)| v.len() as u64).sum();
        ht_refs += this_ref_count;

        // Track all _HT_ alias variants found in column_dict values
        for (_, refs) in &offending {
            for r in refs {
                // Extract the table part: "TABLE"."COL" → TABLE
                let parts: Vec<&str> = r.split('.').collect();
                if parts.len() >= 2 {
                    let table_part = parts[parts.len() - 2].trim_matches('"').to_uppercase();
                    *ht_alias_variants.entry(table_part).or_default() += 1;
                }
            }
        }

        // Detailed trace for first 3 queries
        if detailed < 3 {
            detailed += 1;
            let schema_tables: Vec<String> = schema.keys().cloned().collect();
            let sql_prefix: String = case.sql.chars().take(300).collect();

            println!("{}", "=".repeat(60));
            println!("=== HT_TO_FILTER Query #{detailed}: {} ===", case.name);
            println!("{}", "=".repeat(60));
            println!();
            println!("SQL (first 300 chars):");
            println!("  {}", sql_prefix);
            if case.sql.len() > 300 {
                println!("  ... ({} total chars)", case.sql.len());
            }
            println!();
            println!("Schema tables: {:?}", schema_tables);
            println!();
            println!("Offending column_dict entries ({} refs):", this_ref_count);
            for (col, refs) in &offending {
                println!("  {col}: {:?}", refs);
            }

            // Parse SQL to extract CTE names and subquery aliases
            match Parser::parse_sql(&parser_dialect, &case.sql) {
                Ok(stmts) => {
                    let cte_names = extract_cte_names(&stmts);
                    let subq_aliases = collect_subquery_aliases(&stmts);
                    println!();
                    println!("CTE names: {:?}", cte_names);
                    println!("Subquery aliases: {:?}", subq_aliases);

                    // Check if _HT_TO_FILTER is a known subquery alias
                    let is_cte = cte_names
                        .iter()
                        .any(|n| n.to_uppercase().contains("_HT_TO_FILTER"));
                    let is_subq = subq_aliases
                        .iter()
                        .any(|n| n.to_uppercase().contains("_HT_TO_FILTER"));
                    println!(
                        "_HT_TO_FILTER is CTE: {}, is subquery alias: {}",
                        is_cte, is_subq
                    );
                }
                Err(e) => {
                    println!("Parse error: {e}");
                }
            }

            // Also show full column_dict for context
            println!();
            println!("Full column_dict ({} entries):", result.column_dict.len());
            let mut sorted_dict: Vec<_> = result.column_dict.iter().collect();
            sorted_dict.sort_by_key(|(k, _)| (*k).clone());
            for (col, refs) in &sorted_dict {
                println!("  {col}: {:?}", refs);
            }
            println!();
        }
    }

    println!("{}", "=".repeat(60));
    println!("=== Summary ===");
    println!("{}", "=".repeat(60));
    println!("Total queries processed:              {total}");
    println!("Queries with _HT_TO_FILTER in result: {ht_queries}");
    println!("Total _HT_TO_FILTER references:       {ht_refs}");
    println!();

    println!("_HT_ alias variants in column_dict:");
    let mut sorted_variants: Vec<_> = ht_alias_variants.into_iter().collect();
    sorted_variants.sort_by(|a, b| b.1.cmp(&a.1));
    for (variant, count) in &sorted_variants {
        println!("  {variant:<40} {count:>6}");
    }
}
