//! The query explanation engine.
//!
//! Combines validation, plan walking, lineage extraction, cost analysis,
//! and suggestion generation into a single `explain()` entry point.

use super::cost::analyze_cost;
use super::lineage::extract_lineage;
use super::plan_walker::{count_plan_nodes, walk_plan};
use super::suggestions::generate_suggestions;
use super::types::*;
use crate::complexity::{compute_complexity_v2, estimate_cost};
use crate::linter::engine::lint;
use crate::schema::types::SchemaDefinition;
use crate::validator::engine::validate_with_plan;
use std::time::Instant;

/// Explain a SQL query against a schema definition.
///
/// This is the primary entry point for the M2 explainer. It:
/// 1. Validates the query (reusing the M1 validation pipeline)
/// 2. If valid, walks the logical plan to extract plan steps
/// 3. Extracts column and table lineage
/// 4. Analyzes cost signals and complexity
/// 5. Generates optimization suggestions
///
/// Returns an [`ExplanationResult`] with all analysis data.
///
/// # Example
///
/// ```rust,no_run
/// use altimate_core::explainer::engine::explain;
/// use altimate_core::SchemaDefinition;
///
/// # async fn example() {
/// let schema = SchemaDefinition::from_yaml_file("schema.yaml").unwrap();
/// let result = explain("SELECT u.id, o.total FROM users u JOIN orders o ON u.id = o.user_id", &schema).await;
/// if result.valid {
///     let explanation = result.explanation.unwrap();
///     println!("Summary: {}", explanation.summary);
///     println!("Complexity: {}", explanation.cost_signals.complexity);
///     for step in &explanation.plan_steps {
///         println!("  Step {}: {:?}", step.step, step.operation);
///     }
/// }
/// # }
/// ```
pub async fn explain(sql: &str, schema: &SchemaDefinition) -> ExplanationResult {
    let start = Instant::now();

    let (result, plan) = validate_with_plan(sql, schema).await;

    let validation = ValidationSummary {
        error_count: result.errors.len(),
        warning_count: result.warnings.len(),
        errors: result.errors.clone(),
        warnings: result.warnings.clone(),
    };

    if !result.valid || plan.is_none() {
        let elapsed = start.elapsed();
        return ExplanationResult {
            valid: false,
            explanation: None,
            validation,
            metadata: AnalysisMetadata {
                dialect: schema.dialect.to_string(),
                plan_node_count: 0,
                analysis_time_ms: elapsed.as_millis() as u64,
            },
        };
    }

    let plan = plan.expect("plan should be Some when valid");
    let plan_node_count = count_plan_nodes(&plan);

    // Walk the plan to get structured steps
    let plan_steps = walk_plan(&plan);

    // Extract lineage
    let lineage = extract_lineage(&plan);

    // Analyze cost
    let mut cost_signals = analyze_cost(&plan_steps, &lineage);

    // Compute static cost estimate and v2 complexity score
    let cost_est = estimate_cost(&plan_steps, schema, schema.dialect);
    let lint_result = lint(sql, schema);
    let v2_score = compute_complexity_v2(
        &plan_steps,
        &cost_signals,
        &lint_result.findings,
        Some(&cost_est),
    );
    cost_signals.cost_estimate = Some(cost_est);
    cost_signals.complexity_score_v2 = Some(v2_score);

    // Generate suggestions
    let suggestions = generate_suggestions(&cost_signals, &plan_steps);

    // Build summary string
    let summary = build_summary(&lineage, &cost_signals);

    let elapsed = start.elapsed();

    ExplanationResult {
        valid: true,
        explanation: Some(QueryExplanation {
            summary,
            plan_steps,
            lineage,
            cost_signals,
            suggestions,
        }),
        validation,
        metadata: AnalysisMetadata {
            dialect: schema.dialect.to_string(),
            plan_node_count,
            analysis_time_ms: elapsed.as_millis() as u64,
        },
    }
}

/// Build a human-readable summary of the query.
fn build_summary(lineage: &QueryLineage, signals: &CostSignals) -> String {
    let table_count = lineage.tables.len();
    let join_count = signals.join_count;

    let mut parts = Vec::new();

    // Query type
    if signals.has_aggregation {
        parts.push("aggregate query".to_string());
    } else {
        parts.push("SELECT query".to_string());
    }

    // Tables
    match table_count {
        0 => parts.push("with no tables".to_string()),
        1 => parts.push(format!("reading from table '{}'", lineage.tables[0])),
        n => parts.push(format!("reading from {n} tables")),
    }

    // Joins
    if join_count > 0 {
        let join_word = if join_count == 1 { "join" } else { "joins" };
        parts.push(format!("with {join_count} {join_word}"));
    }

    // Notable features
    let mut features = Vec::new();
    if signals.has_window_function {
        features.push("window functions");
    }
    if signals.has_subquery {
        features.push("subqueries");
    }
    if signals.has_distinct {
        features.push("DISTINCT");
    }
    if signals.has_union {
        features.push("UNION");
    }
    if !features.is_empty() {
        parts.push(format!("using {}", features.join(", ")));
    }

    parts.join(" ")
}
