//! Structured validation error types.
//!
//! Every validation error includes an error code, a kind (for programmatic matching),
//! a human-readable message, a source location, and suggestions for fixing the issue.

use serde::{Deserialize, Serialize};

/// The result of validating a SQL query against a schema.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
    /// Whether the query is valid
    pub valid: bool,

    /// Validation errors (empty if valid)
    pub errors: Vec<ValidationError>,

    /// Validation warnings (non-blocking issues)
    pub warnings: Vec<ValidationWarning>,

    /// Metadata extracted from the query
    pub metadata: QueryMetadata,
}

impl ValidationResult {
    /// Create a result for a valid query
    pub fn ok(metadata: QueryMetadata) -> Self {
        Self {
            valid: true,
            errors: vec![],
            warnings: vec![],
            metadata,
        }
    }

    /// Create a result with errors
    pub fn with_errors(errors: Vec<ValidationError>) -> Self {
        Self {
            valid: false,
            errors,
            warnings: vec![],
            metadata: QueryMetadata::default(),
        }
    }

    /// Create a result for a syntax error
    pub fn syntax_error(message: String, location: Option<SourceLocation>) -> Self {
        Self::with_errors(vec![ValidationError {
            code: ErrorCode::E000,
            kind: ErrorKind::SyntaxError,
            message,
            location,
            suggestions: vec![],
        }])
    }
}

/// A single validation error with context and suggestions.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
    /// Error code (e.g., E001)
    pub code: ErrorCode,

    /// Error kind for programmatic matching
    pub kind: ErrorKind,

    /// Human-readable error message
    pub message: String,

    /// Source location in the SQL query
    pub location: Option<SourceLocation>,

    /// Suggested fixes
    pub suggestions: Vec<Suggestion>,
}

/// A non-blocking warning.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationWarning {
    /// Warning code
    pub code: String,

    /// Warning message
    pub message: String,

    /// Source location
    pub location: Option<SourceLocation>,
}

/// Error codes for categorizing validation errors.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ErrorCode {
    /// Syntax error
    E000,
    /// Table not found
    E001,
    /// Column not found
    E002,
    /// Type mismatch
    E003,
    /// Ambiguous column reference
    E004,
    /// Invalid join condition
    E005,
    /// Unsupported dialect feature
    E006,
    /// Other semantic error
    E099,
}

impl std::fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ErrorCode::E000 => write!(f, "E000"),
            ErrorCode::E001 => write!(f, "E001"),
            ErrorCode::E002 => write!(f, "E002"),
            ErrorCode::E003 => write!(f, "E003"),
            ErrorCode::E004 => write!(f, "E004"),
            ErrorCode::E005 => write!(f, "E005"),
            ErrorCode::E006 => write!(f, "E006"),
            ErrorCode::E099 => write!(f, "E099"),
        }
    }
}

/// Categorized error kinds for programmatic matching.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ErrorKind {
    SyntaxError,
    TableNotFound {
        table: String,
    },
    ColumnNotFound {
        table: Option<String>,
        column: String,
    },
    TypeMismatch {
        expected: String,
        found: String,
    },
    AmbiguousColumn {
        column: String,
        candidates: Vec<String>,
    },
    InvalidJoin {
        reason: String,
    },
    UnsupportedDialectFeature {
        feature: String,
        dialect: String,
    },
    Other {
        detail: String,
    },
}

/// A suggested fix for a validation error.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Suggestion {
    /// Kind of suggestion
    pub kind: SuggestionKind,

    /// Human-readable suggestion message
    pub message: String,

    /// Confidence score (0.0 - 1.0)
    pub confidence: f64,
}

/// Categorized suggestion kinds.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum SuggestionKind {
    /// Fuzzy-matched alternative name
    DidYouMean { name: String },
    /// Qualify with table name
    AddQualification { qualified: String },
    /// Use a different column/function
    UseAlternative { alternative: String },
}

/// Location in the SQL source text.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceLocation {
    pub line: usize,
    pub column: usize,
}

/// Metadata extracted from a validated query.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct QueryMetadata {
    /// Tables referenced in the query
    pub tables_referenced: Vec<String>,

    /// Number of joins
    pub join_count: usize,

    /// Whether the query uses aggregation
    pub has_aggregation: bool,

    /// Whether the query contains subqueries
    pub has_subquery: bool,

    /// Estimated complexity
    pub complexity: Complexity,
}

/// Query complexity estimate.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Complexity {
    #[default]
    Low,
    Medium,
    High,
}
