//! Type definitions for the semantic validation engine.
//!
//! All types derive `Debug, Clone, Serialize, Deserialize` for
//! consistent JSON output and programmatic consumption.

use serde::{Deserialize, Serialize};

/// The result of semantic analysis on a SQL query.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticResult {
    /// Whether the query is structurally valid (parsed and planned successfully)
    pub valid: bool,

    /// Overall semantic correctness score: 0.0 (definitely wrong) to 1.0 (looks correct)
    pub semantic_score: f64,

    /// Detected semantic findings (issues or observations)
    pub findings: Vec<SemanticFinding>,

    /// Names of rules that passed without findings
    pub passed_checks: Vec<String>,

    /// Validation errors if the query could not be parsed/planned
    pub validation_errors: Vec<String>,
}

/// A single semantic finding detected by a rule.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticFinding {
    /// The rule that generated this finding (e.g., "missing_join_condition")
    pub rule: String,

    /// Severity of the finding
    pub severity: SemanticSeverity,

    /// Short human-readable message
    pub message: String,

    /// Detailed explanation of WHY this is a problem
    pub explanation: String,

    /// Suggested fix (if applicable)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub suggestion: Option<String>,

    /// Confidence that this finding is correct: 0.0 to 1.0
    pub confidence: f64,
}

/// Severity levels for semantic findings.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SemanticSeverity {
    /// Definitely wrong — will produce incorrect results
    Error,
    /// Likely wrong or suspicious — should be reviewed
    Warning,
    /// Informational observation — not necessarily wrong
    Info,
}

impl std::fmt::Display for SemanticSeverity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SemanticSeverity::Error => write!(f, "error"),
            SemanticSeverity::Warning => write!(f, "warning"),
            SemanticSeverity::Info => write!(f, "info"),
        }
    }
}

impl SemanticResult {
    /// Create a result for a query that failed validation (could not be planned).
    pub fn invalid(errors: Vec<String>) -> Self {
        Self {
            valid: false,
            semantic_score: 0.0,
            findings: vec![],
            passed_checks: vec![],
            validation_errors: errors,
        }
    }

    /// Create a result with findings and compute the score.
    pub fn with_findings(findings: Vec<SemanticFinding>, passed_checks: Vec<String>) -> Self {
        let score = compute_score(&findings);
        Self {
            valid: true,
            semantic_score: score,
            findings,
            passed_checks,
            validation_errors: vec![],
        }
    }
}

/// Compute a semantic score based on findings.
/// Errors reduce score significantly, warnings less so, info minimally.
fn compute_score(findings: &[SemanticFinding]) -> f64 {
    if findings.is_empty() {
        return 1.0;
    }

    let mut penalty = 0.0;
    for f in findings {
        let severity_weight = match f.severity {
            SemanticSeverity::Error => 0.3,
            SemanticSeverity::Warning => 0.15,
            SemanticSeverity::Info => 0.05,
        };
        penalty += severity_weight * f.confidence;
    }

    (1.0 - penalty).max(0.0)
}
