//! Types for query observability and evaluation.
//!
//! Provides structured evaluation results, quality scorecards, grades,
//! and observability events compatible with platforms like Langfuse and LangSmith.

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

/// Configuration for query evaluation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvalConfig {
    /// Whether to run validation checks (default: true)
    pub enable_validation: bool,

    /// Whether to run lint checks (default: true)
    pub enable_lint: bool,

    /// Whether to run safety/policy checks (default: true)
    pub enable_safety: bool,

    /// Whether to run the explainer for complexity analysis (default: true)
    pub enable_explain: bool,
}

impl Default for EvalConfig {
    fn default() -> Self {
        Self {
            enable_validation: true,
            enable_lint: true,
            enable_safety: true,
            enable_explain: true,
        }
    }
}

/// Result of evaluating a SQL query across all dimensions.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvalResult {
    /// The SQL query that was evaluated
    pub sql: String,

    /// Quality scorecard with dimensional scores
    pub scores: QualityScorecard,

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

    /// Lint result as JSON (if lint was enabled)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lint: Option<serde_json::Value>,

    /// Safety/policy result as JSON (if safety was enabled)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub safety: Option<serde_json::Value>,

    /// Explainer result as JSON (if explain was enabled)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub explain: Option<serde_json::Value>,

    /// Overall letter grade
    pub overall_grade: Grade,

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

/// Quality scorecard with scores across multiple dimensions.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityScorecard {
    /// Syntax validity score: 1.0 = valid, 0.0 = invalid
    pub syntax: f64,

    /// Style/lint score: 1.0 = clean, 0.0 = many findings
    pub style: f64,

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

    /// Complexity score: 1.0 = simple, 0.0 = extremely complex
    pub complexity: f64,

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

impl Default for QualityScorecard {
    fn default() -> Self {
        Self {
            syntax: 0.0,
            style: 1.0,
            safety: 1.0,
            complexity: 1.0,
            overall: 0.0,
        }
    }
}

impl QualityScorecard {
    /// Compute the overall weighted score.
    ///
    /// Formula: syntax * 0.3 + style * 0.2 + safety * 0.2 + complexity * 0.3
    pub fn compute_overall(&mut self) {
        self.overall =
            self.syntax * 0.3 + self.style * 0.2 + self.safety * 0.2 + self.complexity * 0.3;
    }
}

/// Letter grade for overall query quality.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Grade {
    /// Excellent (>= 0.9)
    A,
    /// Good (>= 0.8)
    B,
    /// Acceptable (>= 0.7)
    C,
    /// Poor (>= 0.5)
    D,
    /// Failing (< 0.5)
    F,
}

impl std::fmt::Display for Grade {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Grade::A => write!(f, "A"),
            Grade::B => write!(f, "B"),
            Grade::C => write!(f, "C"),
            Grade::D => write!(f, "D"),
            Grade::F => write!(f, "F"),
        }
    }
}

impl Grade {
    /// Determine grade from an overall score.
    pub fn from_score(score: f64) -> Self {
        if score >= 0.9 {
            Grade::A
        } else if score >= 0.8 {
            Grade::B
        } else if score >= 0.7 {
            Grade::C
        } else if score >= 0.5 {
            Grade::D
        } else {
            Grade::F
        }
    }
}

/// Structured observability event compatible with Langfuse/LangSmith.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObservabilityEvent {
    /// Event type identifier
    pub event_type: String,

    /// ISO 8601 timestamp
    pub timestamp: String,

    /// The SQL query that was evaluated
    pub sql: String,

    /// Quality scores
    pub scores: QualityScorecard,

    /// Letter grade
    pub grade: Grade,

    /// Number of validation errors found
    pub error_count: usize,

    /// Number of lint warnings found
    pub warning_count: usize,

    /// Evaluation duration in milliseconds
    pub duration_ms: u64,

    /// Additional metadata key-value pairs
    pub metadata: HashMap<String, serde_json::Value>,
}
