//! Diagnose queries that produce empty column_dict output.
//!
//! Run: cargo run --example empty_actual --release

use altimate_core::lineage::column_lineage;
use altimate_core::lineage::polyglot_column_lineage;
use altimate_core::schema::types::SqlDialect;
use polyglot_sql::lineage::TerminalKind;
use serde::Deserialize;
use std::collections::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_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(),
    }
}

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("failed to open fixture");
    let reader = std::io::BufReader::with_capacity(1024 * 1024, file);

    let dialect = SqlDialect::Snowflake;

    let mut polyglot_no_edges = 0u64;
    let mut polyglot_has_edges_but_empty = 0u64;
    let mut polyglot_star_only = 0u64;
    let mut polyglot_table_edges = 0u64;
    let mut polyglot_unknown_edges = 0u64;
    let mut total_empty = 0u64;
    let mut polyglot_error = 0u64;

    let mut samples_no_edges: Vec<String> = Vec::new();
    let mut samples_has_edges: Vec<String> = Vec::new();

    for line in reader.lines() {
        let line = line.expect("failed to 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,
        };

        if case.expected.is_empty() {
            continue;
        }

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

        if !result.column_dict.is_empty() {
            continue;
        }
        total_empty += 1;

        // Check raw polyglot edges
        match polyglot_column_lineage(&case.sql, dialect) {
            Err(_) => {
                polyglot_error += 1;
            }
            Ok(base) => {
                if base.edges.is_empty() {
                    polyglot_no_edges += 1;
                    if samples_no_edges.len() < 5 {
                        samples_no_edges.push(format!(
                            "{}: sql(100)={} | expected_cols={}",
                            case.name,
                            &case.sql[..case.sql.len().min(100)],
                            case.expected.len()
                        ));
                    }
                } else {
                    polyglot_has_edges_but_empty += 1;
                    let star_count = base
                        .edges
                        .iter()
                        .filter(|e| matches!(e.terminal_kind, TerminalKind::Star))
                        .count();
                    let table_count = base
                        .edges
                        .iter()
                        .filter(|e| matches!(e.terminal_kind, TerminalKind::Table))
                        .count();
                    let unknown_count = base
                        .edges
                        .iter()
                        .filter(|e| matches!(e.terminal_kind, TerminalKind::Unknown))
                        .count();

                    if star_count > 0 && table_count == 0 && unknown_count == 0 {
                        polyglot_star_only += 1;
                    }
                    if table_count > 0 {
                        polyglot_table_edges += 1;
                    }
                    if unknown_count > 0 {
                        polyglot_unknown_edges += 1;
                    }

                    if samples_has_edges.len() < 10 {
                        let edge_summary: Vec<String> = base
                            .edges
                            .iter()
                            .take(5)
                            .map(|e| {
                                format!(
                                    "{}:{} -> {} ({:?})",
                                    e.source_table,
                                    e.source_column,
                                    e.target_column,
                                    e.terminal_kind
                                )
                            })
                            .collect();
                        samples_has_edges.push(format!(
                            "{}: edges={} (star={}, table={}, unknown={}) | {:?} | sql(80)={}",
                            case.name,
                            base.edges.len(),
                            star_count,
                            table_count,
                            unknown_count,
                            edge_summary,
                            &case.sql[..case.sql.len().min(80)]
                        ));
                    }
                }
            }
        }
    }

    println!("\n=== Empty Actual Diagnosis ===");
    println!("Total empty actual:          {total_empty}");
    println!("polyglot error:              {polyglot_error}");
    println!("polyglot no edges:           {polyglot_no_edges}");
    println!("polyglot has edges but empty: {polyglot_has_edges_but_empty}");
    println!("  star-only edges:           {polyglot_star_only}");
    println!("  has Table edges:           {polyglot_table_edges}");
    println!("  has Unknown edges:         {polyglot_unknown_edges}");

    println!("\n=== Samples: No Edges ===");
    for s in &samples_no_edges {
        println!("  {s}");
    }

    println!("\n=== Samples: Has Edges but Empty Output ===");
    for s in &samples_has_edges {
        println!("  {s}");
    }
}
