use altimate_core::lineage::column_lineage;
use altimate_core::schema::types::SqlDialect;
use std::collections::{BTreeSet, HashMap};
use std::io::BufRead;

fn main() {
    let path = "tests/fixtures/lineage/snowflake_ground_truth.jsonl";
    let file = std::fs::File::open(path).unwrap();
    let reader = std::io::BufReader::new(file);

    let mut star_fail_examples: Vec<(String, String, Vec<String>, Vec<String>)> = Vec::new();
    // sub-patterns
    let mut star_extra_keys = 0; // we have extra output columns
    let mut star_missing_keys = 0; // we're missing output columns
    let mut star_source_diff = 0; // same columns but different sources
    let mut star_both = 0; // both key and source issues
    let mut star_total = 0;

    for line in reader.lines() {
        let line = line.unwrap();
        let rec: serde_json::Value = serde_json::from_str(&line).unwrap();

        let sql = rec["sql"].as_str().unwrap();
        let sql_upper = sql.to_uppercase();

        if !sql_upper.contains("SELECT *") && !sql_upper.contains("SELECT  *") {
            continue;
        }

        let expected_map: HashMap<String, Vec<String>> =
            serde_json::from_value(rec["expected"].clone()).unwrap();
        let schema_val = rec
            .get("schema")
            .cloned()
            .unwrap_or(serde_json::Value::Object(Default::default()));
        let schema: HashMap<String, serde_json::Value> =
            serde_json::from_value(schema_val).unwrap();

        let result = match column_lineage(sql, SqlDialect::Snowflake, &schema, None) {
            Ok(r) => r,
            Err(_) => continue,
        };

        let mut actual_norm: HashMap<String, BTreeSet<String>> = HashMap::new();
        for (k, v) in &result.column_dict {
            let key = k.to_uppercase();
            let sources: BTreeSet<String> = v
                .iter()
                .map(|s| s.replace('"', "").to_uppercase())
                .collect();
            actual_norm.insert(key, sources);
        }

        let mut expected_norm: HashMap<String, BTreeSet<String>> = HashMap::new();
        for (k, v) in &expected_map {
            let key = k.to_uppercase();
            let sources: BTreeSet<String> = v
                .iter()
                .map(|s| s.replace('"', "").to_uppercase())
                .collect();
            expected_norm.insert(key, sources);
        }

        if actual_norm == expected_norm {
            continue;
        }

        star_total += 1;

        let actual_keys: BTreeSet<&String> = actual_norm.keys().collect();
        let expected_keys: BTreeSet<&String> = expected_norm.keys().collect();
        let extra: Vec<_> = actual_keys.difference(&expected_keys).collect();
        let missing: Vec<_> = expected_keys.difference(&actual_keys).collect();

        let has_source_diff = actual_keys
            .intersection(&expected_keys)
            .any(|k| actual_norm.get(*k) != expected_norm.get(*k));

        if !extra.is_empty() && !missing.is_empty() {
            star_both += 1;
        } else if !extra.is_empty() {
            star_extra_keys += 1;
        } else if !missing.is_empty() {
            star_missing_keys += 1;
        } else if has_source_diff {
            star_source_diff += 1;
        }

        if star_fail_examples.len() < 5 && !missing.is_empty() {
            let name = rec["name"].as_str().unwrap().to_string();
            let miss_keys: Vec<String> = missing.iter().map(|k| k.to_string()).collect();
            let extra_keys: Vec<String> = extra.iter().map(|k| k.to_string()).collect();
            star_fail_examples.push((
                name,
                sql[..sql.len().min(300)].to_string(),
                miss_keys,
                extra_keys,
            ));
        }
    }

    eprintln!("=== SELECT * FAILURE ANALYSIS ({star_total} failing queries) ===");
    eprintln!("  Extra output cols:    {star_extra_keys}");
    eprintln!("  Missing output cols:  {star_missing_keys}");
    eprintln!("  Source diff only:     {star_source_diff}");
    eprintln!("  Both extra+missing:   {star_both}");
    eprintln!();
    eprintln!("=== Example SELECT * failures with MISSING columns ===");
    for (name, sql, missing, extra) in &star_fail_examples {
        eprintln!("  {name}:");
        eprintln!("    SQL: {}", &sql[..sql.len().min(200)]);
        eprintln!("    Missing cols: {:?}", missing);
        eprintln!("    Extra cols:   {:?}", extra);
        eprintln!();
    }
}
