//! Type definitions for the query explanation engine.
//!
//! All types derive `Debug, Clone, Serialize, Deserialize` and use
//! serde tagged enums for clean JSON output.

use crate::complexity::types::{ComplexityScoreV2, CostEstimate};
use crate::errors::types::{ValidationError, ValidationWarning};
use serde::{Deserialize, Serialize};

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

    /// The query explanation (present only when valid)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub explanation: Option<QueryExplanation>,

    /// Validation summary with errors and warnings
    pub validation: ValidationSummary,

    /// Analysis metadata
    pub metadata: AnalysisMetadata,
}

/// A full explanation of a valid query.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryExplanation {
    /// Human-readable summary of the query
    pub summary: String,

    /// Step-by-step plan breakdown
    pub plan_steps: Vec<PlanStep>,

    /// Column and table lineage information
    pub lineage: QueryLineage,

    /// Cost signals and complexity analysis
    pub cost_signals: CostSignals,

    /// Optimization suggestions
    pub suggestions: Vec<OptimizationSuggestion>,
}

/// A single step in the logical plan.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlanStep {
    /// Step number (1-indexed)
    pub step: usize,

    /// The operation performed at this step
    pub operation: PlanOperation,

    /// Optional notes about this step
    #[serde(skip_serializing_if = "Option::is_none")]
    pub notes: Option<String>,
}

/// The operation type for a plan step.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PlanOperation {
    /// Scan a table
    TableScan {
        /// Table name
        table: String,
        /// Columns read from the table
        columns_read: Vec<String>,
        /// Pushdown filters
        filters: Vec<String>,
    },
    /// Join two inputs
    Join {
        /// The kind of join
        join_type: JoinKind,
        /// Left input description
        left: String,
        /// Right input description
        right: String,
        /// Join condition
        condition: String,
    },
    /// Filter rows
    Filter {
        /// The filter predicate
        predicate: String,
    },
    /// Aggregate rows
    Aggregate {
        /// GROUP BY expressions
        group_by: Vec<String>,
        /// Aggregate functions
        functions: Vec<String>,
    },
    /// Sort rows
    Sort {
        /// Sort keys
        order_by: Vec<SortKey>,
    },
    /// Limit rows
    Limit {
        /// Maximum rows to fetch
        #[serde(skip_serializing_if = "Option::is_none")]
        limit: Option<usize>,
        /// Rows to skip
        #[serde(skip_serializing_if = "Option::is_none")]
        offset: Option<usize>,
    },
    /// Project columns
    Projection {
        /// Output columns/expressions
        columns: Vec<String>,
    },
    /// Window function
    Window {
        /// Window function expression
        function: String,
        /// PARTITION BY columns
        partition_by: Vec<String>,
        /// ORDER BY columns
        order_by: Vec<String>,
    },
    /// Subquery with alias
    Subquery {
        /// Alias name
        alias: String,
        /// Inner plan steps
        inner_steps: Vec<PlanStep>,
    },
    /// Union of multiple inputs
    Union {
        /// Whether UNION ALL
        all: bool,
        /// Number of branches
        branches: usize,
    },
    /// Distinct operation
    Distinct,
    /// Empty relation (e.g., SELECT without FROM)
    EmptyRelation,
}

/// Join type classification.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum JoinKind {
    /// INNER JOIN
    Inner,
    /// LEFT JOIN
    Left,
    /// RIGHT JOIN
    Right,
    /// FULL OUTER JOIN
    Full,
    /// CROSS JOIN
    Cross,
    /// LEFT SEMI JOIN
    LeftSemi,
    /// LEFT ANTI JOIN
    LeftAnti,
    /// RIGHT SEMI JOIN
    RightSemi,
    /// RIGHT ANTI JOIN
    RightAnti,
}

impl std::fmt::Display for JoinKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            JoinKind::Inner => write!(f, "INNER"),
            JoinKind::Left => write!(f, "LEFT"),
            JoinKind::Right => write!(f, "RIGHT"),
            JoinKind::Full => write!(f, "FULL"),
            JoinKind::Cross => write!(f, "CROSS"),
            JoinKind::LeftSemi => write!(f, "LEFT SEMI"),
            JoinKind::LeftAnti => write!(f, "LEFT ANTI"),
            JoinKind::RightSemi => write!(f, "RIGHT SEMI"),
            JoinKind::RightAnti => write!(f, "RIGHT ANTI"),
        }
    }
}

/// A sort key with direction and null ordering.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortKey {
    /// The column or expression to sort by
    pub column: String,
    /// Sort direction
    pub direction: SortDirection,
    /// Whether nulls sort first
    pub nulls_first: bool,
}

/// Sort direction.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SortDirection {
    /// Ascending
    Asc,
    /// Descending
    Desc,
}

/// Column and table lineage information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryLineage {
    /// Tables referenced in the query
    pub tables: Vec<String>,

    /// Columns read from source tables
    pub columns_read: Vec<ColumnRef>,

    /// Output columns of the query
    pub columns_output: Vec<OutputColumn>,

    /// Join relationships between tables
    pub join_graph: Vec<JoinEdge>,
}

/// A reference to a column in a specific table.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ColumnRef {
    /// Table name
    pub table: String,
    /// Column name
    pub column: String,
}

/// An output column of the query.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputColumn {
    /// The output alias or name
    pub alias: String,
    /// The source of this column
    pub source: ColumnSource,
}

/// Where an output column comes from.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ColumnSource {
    /// Direct reference to a table column
    DirectRef {
        /// Source table
        table: String,
        /// Source column
        column: String,
    },
    /// Result of an aggregation function
    Aggregation {
        /// The aggregate function expression
        function: String,
    },
    /// Result of an expression
    Expression {
        /// The expression string
        expr: String,
    },
    /// Result of a window function
    WindowFunction {
        /// The window function expression
        function: String,
    },
    /// A literal value
    Literal {
        /// The literal value as string
        value: String,
    },
}

/// A join edge in the join graph.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JoinEdge {
    /// Left table in the join
    pub left_table: String,
    /// Right table in the join
    pub right_table: String,
    /// Type of join
    pub join_type: JoinKind,
    /// Pairs of (left_column, right_column) in the ON clause
    pub on_columns: Vec<(String, String)>,
}

/// Cost signals and complexity analysis.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostSignals {
    /// Overall complexity classification
    pub complexity: QueryComplexity,

    /// Numeric complexity score
    pub complexity_score: usize,

    /// Number of joins
    pub join_count: usize,

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

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

    /// Whether the query uses window functions
    pub has_window_function: bool,

    /// Whether the query has a LIMIT clause
    pub has_limit: bool,

    /// Whether the query has a CROSS JOIN
    pub has_cross_join: bool,

    /// Whether SELECT * is used
    pub uses_select_star: bool,

    /// Whether DISTINCT is used
    pub has_distinct: bool,

    /// Whether UNION is used
    pub has_union: bool,

    /// Number of tables referenced
    pub table_count: usize,

    /// Number of filter predicates
    pub filter_count: usize,

    /// Tables scanned without any filter
    pub unfiltered_scans: Vec<String>,

    /// Risk flags for the query
    pub risk_flags: Vec<RiskFlag>,

    /// Static cost estimate (bytes scanned, USD cost)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cost_estimate: Option<CostEstimate>,

    /// Multi-dimensional complexity score (0-100)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub complexity_score_v2: Option<ComplexityScoreV2>,
}

/// Overall query complexity classification.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum QueryComplexity {
    /// Simple query (score 0-2)
    Low,
    /// Moderate complexity (score 3-5)
    Moderate,
    /// High complexity (score 6-9)
    High,
    /// Extreme complexity (score 10+)
    Extreme,
}

impl std::fmt::Display for QueryComplexity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            QueryComplexity::Low => write!(f, "low"),
            QueryComplexity::Moderate => write!(f, "moderate"),
            QueryComplexity::High => write!(f, "high"),
            QueryComplexity::Extreme => write!(f, "extreme"),
        }
    }
}

/// A risk flag for a query.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskFlag {
    /// Severity of the risk
    pub severity: RiskSeverity,
    /// Short flag name
    pub flag: String,
    /// Detailed description
    pub detail: String,
}

/// Risk severity levels.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RiskSeverity {
    /// Informational
    Info,
    /// Warning
    Warning,
    /// Critical risk
    Critical,
}

/// An optimization suggestion.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationSuggestion {
    /// Kind of suggestion
    pub kind: SuggestionType,
    /// Human-readable message
    pub message: String,
    /// Priority level
    pub priority: SuggestionPriority,
}

/// Types of optimization suggestions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SuggestionType {
    /// Add a WHERE filter
    AddFilter,
    /// Add a LIMIT clause
    AddLimit,
    /// Avoid CROSS JOIN
    AvoidCrossJoin,
    /// Replace SELECT * with specific columns
    RemoveSelectStar,
    /// Use specific columns instead of *
    UseSpecificColumns,
    /// Remove redundant DISTINCT
    RemoveRedundantDistinct,
    /// Consider adding an index
    ConsiderIndex,
}

/// Priority levels for suggestions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SuggestionPriority {
    /// Low priority
    Low,
    /// Medium priority
    Medium,
    /// High priority
    High,
}

/// Summary of validation results.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationSummary {
    /// Number of errors
    pub error_count: usize,
    /// Number of warnings
    pub warning_count: usize,
    /// Validation errors
    pub errors: Vec<ValidationError>,
    /// Validation warnings
    pub warnings: Vec<ValidationWarning>,
}

/// Metadata about the analysis process.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisMetadata {
    /// SQL dialect used
    pub dialect: String,
    /// Number of plan nodes processed
    pub plan_node_count: usize,
    /// Analysis time in milliseconds
    pub analysis_time_ms: u64,
}
