use std::{ops::RangeInclusive, rc::Rc};

use super::node::Span;

use derive_more::derive::From;

use crate::{
    antlr::{
        hamelinparser::{
            AggCommandContextAttrs, AppendCommandContextAttrs, CommandContextAll,
            DropCommandContextAttrs, ExplodeCommandContextAttrs, FromCommandContextAttrs,
            GroupClauseContextAttrs, JoinCommandContextAttrs, LetCommandContextAttrs,
            LimitCommandContextAttrs, MatchCommandContextAttrs, NestCommandContextAttrs,
            ParseCommandContextAttrs, SelectCommandContextAttrs, SortCommandContextAttrs,
            UnionCommandContextAttrs, UnnestCommandContextAttrs, WhereCommandContextAttrs,
            WindowCommandContextAttrs, WithinCommandContextAttrs,
        },
        interval,
    },
    err::TranslationError,
    tree::ast::context::{FromCst, ParseContext, TryFromCst},
};

use super::{
    clause::{Assignment, AssignmentClause, FromClause, Selection, SortExpression, TableReference},
    expression::{ErrorExpression, Expression, ExpressionKind, StringLiteral},
    identifier::ParsedIdentifier,
    node::Spannable,
    pattern::Pattern,
};

// ============================================================================
// Command wrapper and enum
// ============================================================================

#[derive(Debug, Clone)]
pub struct Command {
    pub span: Span,
    pub kind: CommandKind,
}

impl Spannable for Command {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl From<CommandKind> for Command {
    fn from(kind: CommandKind) -> Self {
        Self {
            span: Span::NONE,
            kind,
        }
    }
}

impl PartialEq for Command {
    fn eq(&self, other: &Self) -> bool {
        // Compare only the semantic content (kind), ignoring CST
        self.kind == other.kind
    }
}

#[derive(Debug, Clone, PartialEq, From)]
pub enum CommandKind {
    Let(LetCommand),
    Where(WhereCommand),
    Select(SelectCommand),
    Drop(DropCommand),
    Limit(LimitCommand),
    Within(WithinCommand),
    Sort(SortCommand),
    From(FromCommand),
    Union(UnionCommand),
    Parse(ParseCommand),
    Agg(AggCommand),
    Window(WindowCommand),
    Append(AppendCommand),
    Join(JoinCommand),
    Lookup(LookupCommand),
    Explode(ExplodeCommand),
    Unnest(UnnestCommand),
    Rows(RowsCommand),
    Nest(NestCommand),
    Match(MatchCommand),
    Error(Rc<TranslationError>),
}

/// Classification of commands based on their behavior
///
/// This enum provides a centralized way to classify commands as either
/// query operations (that return data) or DML operations (that modify data).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommandClass {
    /// Commands that return data (query operations)
    /// Examples: FROM, WHERE, SELECT, AGG, etc.
    Query,

    /// Commands that modify data (write operations)
    /// Examples: APPEND
    Dml,

    /// Commands that modify table structure
    /// Examples: CREATE
    Ddl,
}

impl CommandKind {
    /// Get the classification of this command
    ///
    /// Returns `CommandClass::Dml` for data modification commands (APPEND),
    /// and `CommandClass::Query` for all other commands.
    pub fn command_class(&self) -> CommandClass {
        match self {
            CommandKind::Append(_) => CommandClass::Dml,
            _ => CommandClass::Query,
        }
    }

    /// Get the display name of this command (for error messages)
    pub fn command_name(&self) -> &'static str {
        match self {
            CommandKind::Let(_) => "LET",
            CommandKind::Where(_) => "WHERE",
            CommandKind::Select(_) => "SELECT",
            CommandKind::Drop(_) => "DROP",
            CommandKind::Limit(_) => "LIMIT",
            CommandKind::Within(_) => "WITHIN",
            CommandKind::Sort(_) => "SORT",
            CommandKind::From(_) => "FROM",
            CommandKind::Union(_) => "UNION",
            CommandKind::Parse(_) => "PARSE",
            CommandKind::Agg(_) => "AGG",
            CommandKind::Window(_) => "WINDOW",
            CommandKind::Append(_) => "APPEND",
            CommandKind::Join(_) => "JOIN",
            CommandKind::Lookup(_) => "LOOKUP",
            CommandKind::Explode(_) => "EXPLODE",
            CommandKind::Unnest(_) => "UNNEST",
            CommandKind::Rows(_) => "ROWS",
            CommandKind::Nest(_) => "NEST",
            CommandKind::Match(_) => "MATCH",
            CommandKind::Error(_) => "ERROR",
        }
    }
}

// ============================================================================
// LET Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct LetCommand {
    pub assignments: Vec<Rc<Assignment>>,
}

impl PartialEq for LetCommand {
    fn eq(&self, other: &Self) -> bool {
        self.assignments == other.assignments
    }
}

// ============================================================================
// WHERE Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct WhereCommand {
    pub condition: Rc<Expression>,
}

impl PartialEq for WhereCommand {
    fn eq(&self, other: &Self) -> bool {
        self.condition == other.condition
    }
}

// ============================================================================
// LIMIT Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct LimitCommand {
    pub count: Rc<Expression>,
}

impl PartialEq for LimitCommand {
    fn eq(&self, other: &Self) -> bool {
        self.count == other.count
    }
}

// ============================================================================
// WITHIN Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct WithinCommand {
    pub duration: Rc<Expression>,
}

impl PartialEq for WithinCommand {
    fn eq(&self, other: &Self) -> bool {
        self.duration == other.duration
    }
}

// ============================================================================
// SORT Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct SortCommand {
    pub expressions: Vec<Rc<SortExpression>>,
}

impl PartialEq for SortCommand {
    fn eq(&self, other: &Self) -> bool {
        self.expressions == other.expressions
    }
}

// ============================================================================
// FROM Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct FromCommand {
    pub clauses: Vec<Rc<FromClause>>,
}

impl PartialEq for FromCommand {
    fn eq(&self, other: &Self) -> bool {
        self.clauses == other.clauses
    }
}

// ============================================================================
// SELECT Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct SelectCommand {
    pub clauses: Vec<Rc<AssignmentClause>>,
}

impl PartialEq for SelectCommand {
    fn eq(&self, other: &Self) -> bool {
        self.clauses == other.clauses
    }
}

// ============================================================================
// DROP Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct DropCommand {
    pub selections: Vec<Rc<Selection>>,
}

impl PartialEq for DropCommand {
    fn eq(&self, other: &Self) -> bool {
        self.selections == other.selections
    }
}

// ============================================================================
// UNION Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct UnionCommand {
    pub clauses: Vec<Rc<FromClause>>,
}

impl PartialEq for UnionCommand {
    fn eq(&self, other: &Self) -> bool {
        self.clauses == other.clauses
    }
}

// ============================================================================
// PARSE Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct ParseCommand {
    pub source: Option<Rc<Expression>>,
    pub pattern: Rc<Expression>,
    pub identifiers: Vec<ParsedIdentifier>,
    pub nodrop: bool,
}

impl PartialEq for ParseCommand {
    fn eq(&self, other: &Self) -> bool {
        self.source == other.source
            && self.pattern == other.pattern
            && self.identifiers == other.identifiers
            && self.nodrop == other.nodrop
    }
}

// ============================================================================
// AGG Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct AggCommand {
    pub clauses: Vec<Rc<AssignmentClause>>,
    pub group_by: Vec<Rc<AssignmentClause>>,
    pub sort: Vec<Rc<SortExpression>>,
}

impl PartialEq for AggCommand {
    fn eq(&self, other: &Self) -> bool {
        self.clauses == other.clauses && self.group_by == other.group_by && self.sort == other.sort
    }
}

// ============================================================================
// WINDOW Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct WindowCommand {
    pub clauses: Vec<Rc<AssignmentClause>>,
    pub group_by: Vec<Rc<AssignmentClause>>,
    pub sort: Vec<Rc<SortExpression>>,
    pub within: Option<Rc<Expression>>,
}

impl PartialEq for WindowCommand {
    fn eq(&self, other: &Self) -> bool {
        self.clauses == other.clauses
            && self.group_by == other.group_by
            && self.sort == other.sort
            && self.within == other.within
    }
}

// ============================================================================
// APPEND Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct AppendCommand {
    pub table: Rc<TableReference>,
    pub distinct_by: Vec<Rc<Selection>>,
}

impl PartialEq for AppendCommand {
    fn eq(&self, other: &Self) -> bool {
        self.table == other.table && self.distinct_by == other.distinct_by
    }
}

// ============================================================================
// JOIN Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct JoinCommand {
    pub other: Rc<FromClause>,
    pub on_condition: Option<Rc<Expression>>,
}

impl PartialEq for JoinCommand {
    fn eq(&self, other: &Self) -> bool {
        self.other == other.other && self.on_condition == other.on_condition
    }
}

// ============================================================================
// LOOKUP Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct LookupCommand {
    pub other: Rc<FromClause>,
    pub on_condition: Option<Rc<Expression>>,
}

impl PartialEq for LookupCommand {
    fn eq(&self, other: &Self) -> bool {
        self.other == other.other && self.on_condition == other.on_condition
    }
}

// ============================================================================
// EXPLODE Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct ExplodeCommand {
    pub clause: Rc<AssignmentClause>,
}

impl PartialEq for ExplodeCommand {
    fn eq(&self, other: &Self) -> bool {
        self.clause == other.clause
    }
}

// ============================================================================
// UNNEST Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct UnnestCommand {
    pub expression: Rc<Expression>,
}

impl PartialEq for UnnestCommand {
    fn eq(&self, other: &Self) -> bool {
        self.expression == other.expression
    }
}

// ============================================================================
// ROWS Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct RowsCommand {
    pub expression: Rc<Expression>,
}

impl PartialEq for RowsCommand {
    fn eq(&self, other: &Self) -> bool {
        self.expression == other.expression
    }
}

// ============================================================================
// NEST Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct NestCommand {
    pub identifier: Rc<ParsedIdentifier>,
}

impl PartialEq for NestCommand {
    fn eq(&self, other: &Self) -> bool {
        self.identifier == other.identifier
    }
}

// ============================================================================
// MATCH Command
// ============================================================================

#[derive(Debug, Clone)]
pub struct MatchCommand {
    pub pattern: Vec<Rc<Pattern>>,
    pub agg: Vec<Rc<AssignmentClause>>,
    pub group_by: Vec<Rc<AssignmentClause>>,
    pub sort: Vec<Rc<SortExpression>>,
    pub within: Option<Rc<Expression>>,
}

impl PartialEq for MatchCommand {
    fn eq(&self, other: &Self) -> bool {
        self.pattern == other.pattern
            && self.agg == other.agg
            && self.group_by == other.group_by
            && self.sort == other.sort
            && self.within == other.within
    }
}

// ============================================================================
// CST Conversions
// ============================================================================

impl FromCst<Rc<CommandContextAll<'static>>> for Command {
    fn from_cst_with_context(cst: Rc<CommandContextAll<'static>>, ctx: &mut ParseContext) -> Self {
        use crate::antlr::hamelinparser::CommandContextAll::*;

        let kind = match cst.as_ref() {
            LetCommandContext(lctx) => {
                let assignments = lctx
                    .assignment_all()
                    .into_iter()
                    .map(|a| Rc::new(Assignment::from_cst_with_context(a, ctx)))
                    .collect();

                LetCommand { assignments }.into()
            }

            WhereCommandContext(wctx) => {
                let condition = if let Some(expr_ctx) = wctx.expression() {
                    Expression::from_cst_with_context(expr_ctx, ctx)
                } else {
                    let err = ctx
                        .error("missing condition in WHERE command")
                        .at(wctx)
                        .emit();
                    Expression::from_kind(ErrorExpression { error: err })
                };

                WhereCommand {
                    condition: Rc::new(condition),
                }
                .into()
            }

            LimitCommandContext(lctx) => {
                let count = if let Some(expr_ctx) = lctx.expression() {
                    Expression::from_cst_with_context(expr_ctx, ctx)
                } else {
                    let err = ctx.error("missing count in LIMIT command").at(lctx).emit();
                    Expression::from_kind(ErrorExpression { error: err })
                };

                LimitCommand {
                    count: Rc::new(count),
                }
                .into()
            }

            WithinCommandContext(wictx) => {
                let duration = if let Some(expr_ctx) = wictx.expression() {
                    Expression::from_cst_with_context(expr_ctx, ctx)
                } else {
                    let err = ctx
                        .error("missing duration in WITHIN command")
                        .at(wictx)
                        .emit();
                    Expression::from_kind(ErrorExpression { error: err })
                };

                WithinCommand {
                    duration: Rc::new(duration),
                }
                .into()
            }

            SortCommandContext(sctx) => {
                let expressions = sctx
                    .sortExpression_all()
                    .into_iter()
                    .map(|e| Rc::new(SortExpression::from_cst_with_context(e, ctx)))
                    .collect();

                SortCommand { expressions }.into()
            }

            FromCommandContext(fctx) => {
                let clauses = fctx
                    .fromClause_all()
                    .into_iter()
                    .map(|c| Rc::new(FromClause::from_cst_with_context(c, ctx)))
                    .collect();

                FromCommand { clauses }.into()
            }

            SelectCommandContext(sctx) => {
                let clauses = sctx
                    .assignmentClause_all()
                    .into_iter()
                    .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    .collect();

                SelectCommand { clauses }.into()
            }

            DropCommandContext(dctx) => {
                let selections = dctx
                    .selection_all()
                    .into_iter()
                    .map(|s| Rc::new(Selection::from_cst_with_context(s, ctx)))
                    .collect();

                DropCommand { selections }.into()
            }

            UnionCommandContext(uctx) => {
                let clauses = uctx
                    .fromClause_all()
                    .into_iter()
                    .map(|c| Rc::new(FromClause::from_cst_with_context(c, ctx)))
                    .collect();

                UnionCommand { clauses }.into()
            }

            ParseCommandContext(pctx) => {
                let source = pctx
                    .src
                    .clone()
                    .map(|expr_ctx| Rc::new(Expression::from_cst_with_context(expr_ctx, ctx)));

                let pattern = pctx
                    .string()
                    .map(|string_ctx| {
                        match StringLiteral::try_from_cst_with_context(&string_ctx, ctx) {
                            Ok(string_lit) => ExpressionKind::from(string_lit).into(),
                            Err(error) => Expression::from_kind(ErrorExpression { error }),
                        }
                    })
                    .unwrap_or_else(|| {
                        let err = ctx
                            .error("missing pattern in PARSE command")
                            .at(pctx)
                            .emit();
                        Expression::from_kind(ErrorExpression { error: err })
                    });

                let identifiers = pctx
                    .identifier_all()
                    .into_iter()
                    .map(|id| ParsedIdentifier::from_cst_with_context(id, ctx))
                    .collect();

                let nodrop = pctx.NODROP().is_some();

                ParseCommand {
                    source,
                    pattern: Rc::new(pattern),
                    identifiers,
                    nodrop,
                }
                .into()
            }

            AggCommandContext(actx) => {
                let clauses = actx
                    .assignmentClause_all()
                    .into_iter()
                    .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    .collect();

                let group_by = actx
                    .groupClause_all()
                    .into_iter()
                    .filter_map(|gc| gc.assignmentClause())
                    .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    .collect();

                let sort = actx
                    .sortExpression_all()
                    .into_iter()
                    .map(|s| Rc::new(SortExpression::from_cst_with_context(s, ctx)))
                    .collect();

                AggCommand {
                    clauses,
                    group_by,
                    sort,
                }
                .into()
            }

            WindowCommandContext(wctx) => {
                let clauses = wctx
                    .assignmentClause_all()
                    .into_iter()
                    .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    .collect();

                let group_by = wctx
                    .groupClause_all()
                    .into_iter()
                    .filter_map(|gc| gc.assignmentClause())
                    .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    .collect();

                let sort = wctx
                    .sortExpression_all()
                    .into_iter()
                    .map(|s| Rc::new(SortExpression::from_cst_with_context(s, ctx)))
                    .collect();

                let within = wctx
                    .within
                    .clone()
                    .map(|expr_ctx| Rc::new(Expression::from_cst_with_context(expr_ctx, ctx)));

                WindowCommand {
                    clauses,
                    group_by,
                    sort,
                    within,
                }
                .into()
            }

            AppendCommandContext(apctx) => {
                let table = apctx
                    .tableReference()
                    .map(|tr| TableReference::from_cst_with_context(tr, ctx))
                    .unwrap_or_else(|| TableReference {
                        span: Span::NONE,
                        identifier: ctx
                            .error("missing table reference in APPEND command")
                            .at(apctx)
                            .emit()
                            .into(),
                    });

                let distinct_by = apctx
                    .selection_all()
                    .into_iter()
                    .map(|s| Rc::new(Selection::from_cst_with_context(s, ctx)))
                    .collect();

                AppendCommand {
                    table: Rc::new(table),
                    distinct_by,
                }
                .into()
            }

            JoinCommandContext(jctx) => {
                let is_lookup = jctx.LOOKUP_COMMAND().is_some();

                let other = Rc::new(
                    jctx.fromClause()
                        .map(|fc| FromClause::from_cst_with_context(fc, ctx))
                        .unwrap_or_else(|| {
                            Rc::new(TableReference {
                                span: Span::NONE,
                                identifier: ctx
                                    .error("missing from clause in JOIN/LOOKUP command")
                                    .at(jctx)
                                    .emit()
                                    .into(),
                            })
                            .into()
                        }),
                );

                let on_condition = jctx
                    .on
                    .clone()
                    .map(|expr_ctx| Rc::new(Expression::from_cst_with_context(expr_ctx, ctx)));

                if is_lookup {
                    LookupCommand {
                        other,
                        on_condition,
                    }
                    .into()
                } else {
                    JoinCommand {
                        other,
                        on_condition,
                    }
                    .into()
                }
            }

            ExplodeCommandContext(exctx) => {
                let clause = Rc::new(
                    exctx
                        .assignmentClause()
                        .map(|ac| AssignmentClause::from_cst_with_context(ac, ctx))
                        .unwrap_or_else(|| {
                            AssignmentClause::Error(
                                ctx.error("missing clause in EXPLODE command")
                                    .at(exctx)
                                    .emit(),
                            )
                        }),
                );

                ExplodeCommand { clause }.into()
            }

            UnnestCommandContext(unctx) => {
                let is_rows = unctx.ROWS_COMMAND().is_some();

                let expression = if let Some(expr_ctx) = unctx.expression() {
                    Expression::from_cst_with_context(expr_ctx, ctx)
                } else {
                    let err = ctx
                        .error("missing expression in UNNEST/ROWS command")
                        .at(unctx)
                        .emit();
                    Expression::from_kind(ErrorExpression { error: err })
                };

                let rc_expression = Rc::new(expression);

                if is_rows {
                    RowsCommand {
                        expression: rc_expression.clone(),
                    }
                    .into()
                } else {
                    UnnestCommand {
                        expression: rc_expression,
                    }
                    .into()
                }
            }

            NestCommandContext(nctx) => {
                let identifier = Rc::new(
                    nctx.identifier()
                        .map(|id| ParsedIdentifier::from_cst_with_context(id, ctx))
                        .unwrap_or_else(|| {
                            ctx.error("missing identifier in NEST command")
                                .at(nctx)
                                .emit()
                                .into()
                        }),
                );

                NestCommand { identifier }.into()
            }

            MatchCommandContext(mctx) => {
                let pattern = mctx
                    .pattern_all()
                    .into_iter()
                    .map(|p| Rc::new(Pattern::from_cst_with_context(p, ctx)))
                    .collect();

                // AGG clauses (assignmentClause directly under MATCH, not via groupClause)
                let agg = mctx
                    .assignmentClause_all()
                    .into_iter()
                    .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    .collect();

                let group_by = mctx
                    .groupClause_all()
                    .into_iter()
                    .filter_map(|gc| {
                        gc.assignmentClause()
                            .map(|c| Rc::new(AssignmentClause::from_cst_with_context(c, ctx)))
                    })
                    .collect();

                let sort = mctx
                    .sortExpression_all()
                    .into_iter()
                    .map(|sort_ctx| Rc::new(SortExpression::from_cst_with_context(sort_ctx, ctx)))
                    .collect();

                let within = mctx.within.as_ref().map(|expr_ctx| {
                    Rc::new(Expression::from_cst_with_context(expr_ctx.clone(), ctx))
                });

                MatchCommand {
                    pattern,
                    agg,
                    group_by,
                    sort,
                    within,
                }
                .into()
            }

            _ => CommandKind::Error(
                ctx.error("command not yet implemented in AST")
                    .at(cst.as_ref())
                    .emit(),
            ),
        };

        Command {
            span: interval(cst.as_ref()).into(),
            kind,
        }
    }
}
