//! CI/CD complexity gate with SARIF output.
//!
//! Checks queries against a complexity threshold and generates
//! SARIF 2.1.0 output for GitHub Code Scanning integration.

use crate::complexity::cost_estimator::estimate_cost;
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;

/// Check a batch of SQL queries against a complexity threshold.
///
/// Returns a [`GateResult`] indicating whether all queries passed.
pub async fn check_complexity_gate(
    queries: &[(String, String)], // (file_path, sql)
    schema: &SchemaDefinition,
    threshold: u32,
) -> GateResult {
    let mut results = Vec::new();

    for (file, sql) in queries {
        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![],
                    },
                ),
            )
        };

        let cost_est = estimate_cost(&steps, schema, schema.dialect);
        let complexity = compute_complexity_v2(
            &steps,
            &cost_signals,
            &lint_result.findings,
            Some(&cost_est),
        );

        let passed = complexity.total <= threshold;
        let sql_preview = if sql.len() > 80 {
            format!("{}...", &sql[..77])
        } else {
            sql.clone()
        };

        results.push(QueryGateResult {
            file: file.clone(),
            sql_preview,
            score: complexity.total,
            tier: complexity.tier,
            passed,
            cost_estimate: Some(cost_est),
            complexity,
        });
    }

    let total = results.len();
    let passed_count = results.iter().filter(|r| r.passed).count();
    let failed_count = total - passed_count;
    let avg_score = if total > 0 {
        results.iter().map(|r| r.score as f64).sum::<f64>() / total as f64
    } else {
        0.0
    };
    let max_score = results.iter().map(|r| r.score).max().unwrap_or(0);

    let all_passed = failed_count == 0;

    GateResult {
        passed: all_passed,
        threshold,
        results,
        summary: GateSummary {
            total,
            passed: passed_count,
            failed: failed_count,
            avg_score,
            max_score,
        },
    }
}

/// Format a gate result as SARIF 2.1.0 JSON for GitHub Code Scanning.
pub fn format_sarif(gate_result: &GateResult) -> serde_json::Value {
    let mut results = Vec::new();

    for qr in &gate_result.results {
        if !qr.passed {
            results.push(serde_json::json!({
                "ruleId": "altimate-core/complexity",
                "level": if qr.score > 80 { "error" } else { "warning" },
                "message": {
                    "text": format!(
                        "Query complexity score {} ({}) exceeds threshold {}",
                        qr.score, qr.tier, gate_result.threshold
                    )
                },
                "locations": [{
                    "physicalLocation": {
                        "artifactLocation": {
                            "uri": qr.file,
                        },
                        "region": {
                            "startLine": 1
                        }
                    }
                }],
                "properties": {
                    "score": qr.score,
                    "tier": qr.tier.to_string(),
                    "structural": qr.complexity.structural.score,
                    "semantic": qr.complexity.semantic.score,
                    "cost": qr.complexity.cost.score,
                }
            }));
        }
    }

    serde_json::json!({
        "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
        "version": "2.1.0",
        "runs": [{
            "tool": {
                "driver": {
                    "name": "altimate-core",
                    "version": env!("CARGO_PKG_VERSION"),
                    "informationUri": "https://github.com/AltimateAI/altimate-core",
                    "rules": [{
                        "id": "altimate-core/complexity",
                        "shortDescription": {
                            "text": "SQL query complexity exceeds threshold"
                        },
                        "fullDescription": {
                            "text": format!(
                                "Query complexity score exceeds the configured threshold of {}. Consider simplifying the query.",
                                gate_result.threshold
                            )
                        },
                        "defaultConfiguration": {
                            "level": "warning"
                        }
                    }]
                }
            },
            "results": results
        }]
    })
}

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

    fn simple_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: None,
                clustering_keys: vec![],
            },
        );
        SchemaDefinition {
            version: "1".to_string(),
            dialect: SqlDialect::Generic,
            tables,
            database: None,
            schema_name: None,
            glossary: None,
        }
    }

    #[tokio::test]
    async fn test_gate_simple_query_passes() {
        let schema = simple_schema();
        let queries = vec![("test.sql".to_string(), "SELECT id FROM users".to_string())];
        let result = check_complexity_gate(&queries, &schema, 60).await;
        assert!(result.passed);
        assert_eq!(result.summary.total, 1);
        assert_eq!(result.summary.passed, 1);
    }

    #[tokio::test]
    async fn test_gate_with_zero_threshold_fails() {
        let schema = simple_schema();
        let queries = vec![("test.sql".to_string(), "SELECT id FROM users".to_string())];
        let result = check_complexity_gate(&queries, &schema, 0).await;
        // Even a simple query might have score > 0
        // This test verifies the gate works
        assert_eq!(result.summary.total, 1);
    }

    #[test]
    fn test_sarif_output_format() {
        let gate_result = GateResult {
            passed: false,
            threshold: 60,
            results: vec![QueryGateResult {
                file: "query.sql".to_string(),
                sql_preview: "SELECT * FROM a CROSS JOIN b".to_string(),
                score: 75,
                tier: ComplexityTier::VeryComplex,
                passed: false,
                cost_estimate: None,
                complexity: ComplexityScoreV2 {
                    total: 75,
                    structural: DimensionScore {
                        score: 30,
                        max: 40,
                        factors: vec![],
                    },
                    semantic: DimensionScore {
                        score: 20,
                        max: 30,
                        factors: vec![],
                    },
                    cost: DimensionScore {
                        score: 25,
                        max: 30,
                        factors: vec![],
                    },
                    tier: ComplexityTier::VeryComplex,
                    factors: vec![],
                },
            }],
            summary: GateSummary {
                total: 1,
                passed: 0,
                failed: 1,
                avg_score: 75.0,
                max_score: 75,
            },
        };

        let sarif = format_sarif(&gate_result);
        assert_eq!(sarif["version"], "2.1.0");
        assert!(!sarif["runs"][0]["results"].as_array().unwrap().is_empty());
    }

    #[test]
    fn test_sarif_no_failures() {
        let gate_result = GateResult {
            passed: true,
            threshold: 60,
            results: vec![],
            summary: GateSummary {
                total: 0,
                passed: 0,
                failed: 0,
                avg_score: 0.0,
                max_score: 0,
            },
        };

        let sarif = format_sarif(&gate_result);
        assert!(sarif["runs"][0]["results"].as_array().unwrap().is_empty());
    }
}
