//! Deep dive: show full SQL + expected + actual for superset failures.
//!
//! Run: cargo run --example superset_deep_dive --release 2>&1 | head -500

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

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

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

fn normalize(map: &HashMap<String, Vec<String>>) -> HashMap<String, Vec<String>> {
    map.iter()
        .map(|(k, v)| {
            let mut s = v.clone();
            s.sort();
            s.dedup();
            (k.clone(), s)
        })
        .collect()
}

fn short_names(map: &HashMap<String, Vec<String>>) -> HashMap<String, Vec<String>> {
    map.iter()
        .map(|(k, v)| {
            let n: Vec<String> = v
                .iter()
                .map(|r| {
                    let parts: Vec<&str> = r.split('.').map(|p| p.trim_matches('"')).collect();
                    if parts.len() >= 3 {
                        format!(
                            "\"{}\".\"{}\"",
                            parts[parts.len() - 2],
                            parts[parts.len() - 1]
                        )
                    } else {
                        r.clone()
                    }
                })
                .collect();
            let mut s = n;
            s.sort();
            s.dedup();
            (k.clone(), s)
        })
        .collect()
}

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");
    let reader = std::io::BufReader::with_capacity(1024 * 1024, file);
    let dialect = SqlDialect::Snowflake;
    let mut printed = 0u32;

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

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

        let actual = short_names(&result.column_dict);
        let expected = normalize(&case.expected);
        if actual == expected {
            continue;
        }

        // Check: is this a pure superset (all wrong cols have extra sources only)?
        let mut is_pure_superset = true;
        let mut any_wrong = false;
        let mut wrong_cols: Vec<(String, Vec<String>, Vec<String>)> = Vec::new();
        for (col, exp_srcs) in &expected {
            match actual.get(col) {
                Some(act_srcs) if act_srcs == exp_srcs => {}
                Some(act_srcs) => {
                    any_wrong = true;
                    let es: BTreeSet<_> = exp_srcs.iter().collect();
                    let as_: BTreeSet<_> = act_srcs.iter().collect();
                    if !as_.is_superset(&es) {
                        is_pure_superset = false;
                        break;
                    }
                    wrong_cols.push((col.clone(), exp_srcs.clone(), act_srcs.clone()));
                }
                None => {
                    is_pure_superset = false;
                    break;
                }
            }
        }
        // Also check no missing cols
        for col in actual.keys() {
            if !expected.contains_key(col) {
                is_pure_superset = false;
                break;
            }
        }
        if !any_wrong {
            is_pure_superset = false;
        }
        if !is_pure_superset {
            continue;
        }

        // Show full SQL for first 15 pure superset failures
        if printed >= 15 {
            continue;
        }
        printed += 1;

        eprintln!("\n======================================================================");
        eprintln!("=== PURE SUPERSET #{} [{}] ===", printed, case.name);
        eprintln!("=== SQL (full): ===");
        eprintln!("{}", case.sql);
        for (col, exp, act) in &wrong_cols {
            let es: BTreeSet<_> = exp.iter().collect();
            let as_: BTreeSet<_> = act.iter().collect();
            let extra: Vec<_> = as_.difference(&es).collect();
            eprintln!("\n  WRONG COL: {}", col);
            eprintln!("  Expected sources: {:?}", exp);
            eprintln!("  Actual sources:   {:?}", act);
            eprintln!("  EXTRA sources:    {:?}", extra);
        }
    }

    if printed == 0 {
        eprintln!("No pure superset failures found!");
    }
}
