//! Types for the iterative correction protocol.
//!
//! The correction protocol implements a "propose-verify-refine" loop that
//! agentic text-to-SQL systems use: validate → fix → lint → re-validate,
//! tracking all iterations and computing a quality score.

use serde::{Deserialize, Serialize};

/// Configuration for the correction protocol.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrectionConfig {
    /// Maximum number of correction iterations (default: 5)
    pub max_iterations: usize,

    /// Minimum confidence threshold for accepting a fix (default: 0.7)
    pub min_confidence: f64,

    /// Whether to run lint checks on the corrected SQL (default: true)
    pub enable_lint_checks: bool,

    /// Whether to run safety/policy checks on the corrected SQL (default: true)
    pub enable_safety_scan: bool,
}

impl Default for CorrectionConfig {
    fn default() -> Self {
        Self {
            max_iterations: 5,
            min_confidence: 0.7,
            enable_lint_checks: true,
            enable_safety_scan: true,
        }
    }
}

/// Result of the correction protocol.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrectionResult {
    /// Final status of the correction attempt
    pub status: CorrectionStatus,

    /// The original SQL that was submitted
    pub original_sql: String,

    /// The corrected SQL (if any correction was made)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub corrected_sql: Option<String>,

    /// All correction iterations performed
    pub iterations: Vec<CorrectionIteration>,

    /// Final validation result as JSON (if available)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub final_validation: Option<serde_json::Value>,

    /// Quality score of the final SQL
    pub final_score: QualityScore,

    /// Total time taken in milliseconds
    pub total_time_ms: u64,
}

/// Status of the correction protocol outcome.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CorrectionStatus {
    /// The SQL was already valid, no corrections needed
    AlreadyValid,
    /// The SQL was fixed successfully
    Fixed,
    /// The SQL was partially fixed (some errors remain)
    PartialFix,
    /// The SQL could not be fixed
    Unfixable,
}

impl std::fmt::Display for CorrectionStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CorrectionStatus::AlreadyValid => write!(f, "already_valid"),
            CorrectionStatus::Fixed => write!(f, "fixed"),
            CorrectionStatus::PartialFix => write!(f, "partial_fix"),
            CorrectionStatus::Unfixable => write!(f, "unfixable"),
        }
    }
}

/// A single iteration of the correction loop.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrectionIteration {
    /// Iteration number (1-indexed)
    pub iteration: usize,

    /// The SQL at the start of this iteration
    pub input_sql: String,

    /// Validation errors found at the start of this iteration
    pub validation_errors: Vec<String>,

    /// The fix that was applied (if any)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fix_applied: Option<String>,

    /// Description of the fix applied
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fix_description: Option<String>,

    /// Result of this iteration
    pub result: IterationResult,
}

/// Result of a single correction iteration.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IterationResult {
    /// The fix resolved all errors
    Fixed,
    /// The fix did not resolve all errors
    StillInvalid,
    /// The fix introduced new errors
    NewErrors,
    /// No fix was attempted (e.g., no available fix)
    Skipped,
}

impl std::fmt::Display for IterationResult {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            IterationResult::Fixed => write!(f, "fixed"),
            IterationResult::StillInvalid => write!(f, "still_invalid"),
            IterationResult::NewErrors => write!(f, "new_errors"),
            IterationResult::Skipped => write!(f, "skipped"),
        }
    }
}

/// Quality score of a SQL query across multiple dimensions.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityScore {
    /// Whether the SQL is syntactically valid
    pub syntax_valid: bool,

    /// Lint score: 1.0 = no findings, 0.0 = critical issues
    pub lint_score: f64,

    /// Safety/policy score: 1.0 = no violations, 0.0 = critical violations
    pub safety_score: f64,

    /// Complexity score: 1.0 = simple, 0.0 = extreme complexity
    pub complexity_score: f64,

    /// Overall weighted score (0.0 - 1.0)
    pub overall: f64,
}

impl Default for QualityScore {
    fn default() -> Self {
        Self {
            syntax_valid: false,
            lint_score: 0.0,
            safety_score: 1.0,
            complexity_score: 1.0,
            overall: 0.0,
        }
    }
}

impl QualityScore {
    /// Compute the overall weighted score.
    ///
    /// Formula: syntax (0 or 1) * 0.3 + lint * 0.2 + safety * 0.2 + complexity * 0.3
    pub fn compute_overall(&mut self) {
        let syntax = if self.syntax_valid { 1.0 } else { 0.0 };
        self.overall = syntax * 0.3
            + self.lint_score * 0.2
            + self.safety_score * 0.2
            + self.complexity_score * 0.3;
    }
}
