//! Cross-dialect complexity comparison.
//!
//! Compares complexity scores and cost estimates between two SQL dialects,
//! useful for migration planning.

use crate::complexity::cost_estimator::estimate_cost;
use crate::complexity::cost_profiles::CostProfile;
use crate::complexity::scoring::compute_complexity_v2;
use crate::complexity::types::*;
use crate::explainer::engine::explain;
use crate::linter::engine::lint;
use crate::schema::types::{SchemaDefinition, SqlDialect};

/// Compare complexity and cost of a query across two dialects.
pub async fn compare_dialects(
    sql: &str,
    schema: &SchemaDefinition,
    from_dialect: SqlDialect,
    to_dialect: SqlDialect,
) -> DialectComparisonResult {
    // Run explanation and linting
    let explanation = explain(sql, schema).await;
    let lint_result = lint(sql, schema);

    let (steps, cost_signals) = if let Some(ref expl) = explanation.explanation {
        (expl.plan_steps.clone(), expl.cost_signals.clone())
    } else {
        (
            vec![],
            crate::explainer::cost::analyze_cost(
                &[],
                &crate::explainer::types::QueryLineage {
                    tables: vec![],
                    columns_read: vec![],
                    columns_output: vec![],
                    join_graph: vec![],
                },
            ),
        )
    };

    // Estimate cost for each dialect
    let from_cost_est = estimate_cost(&steps, schema, from_dialect);
    let to_cost_est = estimate_cost(&steps, schema, to_dialect);

    // Compute complexity for each dialect (structural/semantic same, cost differs)
    let from_complexity = compute_complexity_v2(
        &steps,
        &cost_signals,
        &lint_result.findings,
        Some(&from_cost_est),
    );
    let to_complexity = compute_complexity_v2(
        &steps,
        &cost_signals,
        &lint_result.findings,
        Some(&to_cost_est),
    );

    // Generate dialect-specific insights
    let insights = generate_insights(from_dialect, to_dialect, &from_cost_est, &to_cost_est);

    DialectComparisonResult {
        sql: sql.to_string(),
        from_dialect: from_dialect.to_string(),
        to_dialect: to_dialect.to_string(),
        from_complexity,
        to_complexity,
        from_cost: Some(from_cost_est),
        to_cost: Some(to_cost_est),
        insights,
    }
}

/// Generate dialect-specific insights for the comparison.
fn generate_insights(
    from: SqlDialect,
    to: SqlDialect,
    from_est: &CostEstimate,
    to_est: &CostEstimate,
) -> Vec<String> {
    let mut insights = Vec::new();

    let from_profile = CostProfile::for_dialect(from);
    let to_profile = CostProfile::for_dialect(to);

    // Cost comparison
    if from_est.cost_usd > 0.0 && to_est.cost_usd > 0.0 {
        let ratio = to_est.cost_usd / from_est.cost_usd;
        if ratio < 0.5 {
            insights.push(format!(
                "Migration to {} could reduce cost by {:.0}% (${:.4} → ${:.4})",
                to,
                (1.0 - ratio) * 100.0,
                from_est.cost_usd,
                to_est.cost_usd
            ));
        } else if ratio > 2.0 {
            insights.push(format!(
                "Migration to {} could increase cost by {:.0}% (${:.4} → ${:.4})",
                to,
                (ratio - 1.0) * 100.0,
                from_est.cost_usd,
                to_est.cost_usd
            ));
        }
    }

    // CTE materialization
    if !from_profile.ctes_materialized && to_profile.ctes_materialized {
        insights.push(format!(
            "{to} materializes CTEs — repeated CTE references may increase cost",
        ));
    }

    // Window function cost
    if !from_profile.cheap_window_functions && to_profile.cheap_window_functions {
        insights.push(format!(
            "{to} has optimized window functions — may reduce cost",
        ));
    } else if from_profile.cheap_window_functions && !to_profile.cheap_window_functions {
        insights.push(format!(
            "{to} may have higher window function costs than {from}",
        ));
    }

    insights
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::schema::types::*;
    use std::collections::HashMap;

    fn test_schema() -> SchemaDefinition {
        let mut tables = HashMap::new();
        tables.insert(
            "users".to_string(),
            TableDef {
                description: None,
                database: None,
                schema: None,
                columns: vec![ColumnDef {
                    name: "id".to_string(),
                    data_type: "INT".to_string(),
                    nullable: false,
                    description: None,
                    tags: vec![],
                    synonyms: vec![],

                    statistics: None,
                }],
                primary_key: None,
                foreign_keys: vec![],
                statistics: Some(TableStatistics {
                    row_count: Some(1_000_000),
                    avg_row_bytes: Some(200),
                    partitioned: false,
                    partition_columns: vec![],
                    correlated_columns: vec![],
                    ..Default::default()
                }),
                clustering_keys: vec![],
            },
        );
        SchemaDefinition {
            version: "1".to_string(),
            dialect: SqlDialect::Generic,
            tables,
            database: None,
            schema_name: None,
            glossary: None,
        }
    }

    #[tokio::test]
    async fn test_same_dialect_comparison() {
        let schema = test_schema();
        let result = compare_dialects(
            "SELECT id FROM users",
            &schema,
            SqlDialect::BigQuery,
            SqlDialect::BigQuery,
        )
        .await;
        assert_eq!(result.from_complexity.total, result.to_complexity.total);
    }

    #[tokio::test]
    async fn test_cross_dialect_comparison() {
        let schema = test_schema();
        let result = compare_dialects(
            "SELECT id FROM users",
            &schema,
            SqlDialect::BigQuery,
            SqlDialect::Snowflake,
        )
        .await;
        assert_eq!(result.from_dialect, "bigquery");
        assert_eq!(result.to_dialect, "snowflake");
        // Cost should differ due to different pricing
        assert!(result.from_cost.is_some());
        assert!(result.to_cost.is_some());
    }

    #[test]
    fn test_cte_materialization_insight() {
        let from = CostEstimate {
            bytes_scanned: 1000,
            cost_usd: 0.001,
            cost_tier: CostTier::Cheap,
            table_breakdown: vec![],
            warnings: vec![],
            disclaimer: None,
            cost_range: None,
            recommendations: vec![],
            confidence: EstimationConfidence::High,
            confidence_factors: vec![],
            estimation_method: None,
            time_based_cost: None,
        };
        let to = from.clone();
        let insights = generate_insights(SqlDialect::Postgres, SqlDialect::Snowflake, &from, &to);
        assert!(insights.iter().any(|i| i.contains("materializes CTEs")));
    }
}
