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

use super::node::Span;

use derive_more::derive::{From, TryUnwrap};

use crate::{
    antlr::{
        hamelinparser::{
            AssignmentClauseContextAll, AssignmentClauseContextAttrs, AssignmentContextAll,
            AssignmentContextAttrs, FromClauseContextAll, FromClauseContextAttrs,
            SelectionContextAll, SelectionContextAttrs, SortExpressionContextAll,
            SortExpressionContextAttrs, TableAliasContextAttrs, TableReferenceContextAll,
            TableReferenceContextAttrs,
        },
        interval,
    },
    err::TranslationError,
    tree::ast::{
        context::{FromCst, ParseContext},
        identifier::Identifier,
    },
};

use super::{
    expression::{ErrorExpression, Expression},
    identifier::{ParsedIdentifier, ParsedSimpleIdentifier, SimpleIdentifier},
    node::Spannable,
};
use antlr_rust::tree::ParseTree;

// ============================================================================
// Assignment
// ============================================================================

#[derive(Debug, Clone)]
pub struct Assignment {
    pub span: Span,
    pub identifier: ParsedIdentifier,
    pub expression: Rc<Expression>,
}

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

impl Assignment {
    pub fn from_parts(identifier: ParsedIdentifier, expression: Expression) -> Self {
        Self {
            span: Span::NONE,
            identifier,
            expression: Rc::new(expression),
        }
    }
}

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

impl FromCst<Rc<AssignmentContextAll<'static>>> for Assignment {
    fn from_cst_with_context(
        cst: Rc<AssignmentContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        let identifier = cst
            .identifier()
            .map(|id_cst| ParsedIdentifier::from_cst_with_context(id_cst, ctx))
            .unwrap_or_else(|| {
                ctx.error("missing identifier in assignment")
                    .at(cst.as_ref())
                    .emit()
                    .into()
            });

        let expression = if let Some(expr_ctx) = cst.expression() {
            Expression::from_cst_with_context(expr_ctx, ctx)
        } else {
            let err = ctx
                .error("missing expression in assignment")
                .at(cst.as_ref())
                .emit();
            Expression::from_kind(ErrorExpression { error: err })
        };

        Self {
            span: interval(cst.as_ref()).into(),
            identifier,
            expression: Rc::new(expression),
        }
    }
}

// ============================================================================
// AssignmentClause
// ============================================================================

/// An assignment clause is either an explicit assignment (identifier = expression)
/// or an error. Bare expressions are desugared to assignments at parse time
/// by inferring the identifier from the expression structure.
#[derive(Debug, Clone, From, PartialEq, TryUnwrap)]
pub enum AssignmentClause {
    Assignment(Assignment),
    Error(Rc<TranslationError>),
}

impl Spannable for AssignmentClause {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        match self {
            AssignmentClause::Assignment(a) => a.span(),
            AssignmentClause::Error(_) => None,
        }
    }
}

impl FromCst<Rc<AssignmentClauseContextAll<'static>>> for AssignmentClause {
    fn from_cst_with_context(
        cst: Rc<AssignmentClauseContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        if let Some(assignment_ctx) = cst.assignment() {
            Assignment::from_cst_with_context(assignment_ctx, ctx).into()
        } else if let Some(expr_ctx) = cst.expression() {
            // Bare expression: desugar to assignment by inferring identifier
            let expression = Expression::from_cst_with_context(expr_ctx.clone(), ctx);

            // Try to infer identifier from AST structure, fall back to source text
            let identifier = Identifier::infer_from_expression(&expression)
                .unwrap_or_else(|| SimpleIdentifier::new(expr_ctx.get_text()).into());

            Assignment::from_parts(identifier, expression).into()
        } else {
            ctx.error("invalid assignment clause")
                .at(cst.as_ref())
                .emit()
                .into()
        }
    }
}

// ============================================================================
// FromClause
// ============================================================================

#[derive(Debug, Clone)]
pub struct TableAlias {
    pub span: Span,
    pub alias: ParsedSimpleIdentifier,
    pub table: TableReference,
}

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

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

#[derive(Debug, Clone)]
pub struct TableReference {
    pub span: Span,
    pub identifier: ParsedIdentifier,
}

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

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

impl FromCst<Rc<TableReferenceContextAll<'static>>> for TableReference {
    fn from_cst_with_context(
        cst: Rc<TableReferenceContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        let identifier = cst
            .identifier()
            .map(|id| ParsedIdentifier::from_cst_with_context(id, ctx))
            .unwrap_or_else(|| {
                ctx.error("missing table identifier")
                    .at(cst.as_ref())
                    .emit()
                    .into()
            });

        Self {
            span: interval(cst.as_ref()).into(),
            identifier,
        }
    }
}

#[derive(Debug, Clone, From, PartialEq, TryUnwrap)]
pub enum FromClause {
    TableAlias(Rc<TableAlias>),
    TableReference(Rc<TableReference>),
}

impl Spannable for FromClause {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        match self {
            FromClause::TableAlias(ta) => ta.span(),
            FromClause::TableReference(tr) => tr.span(),
        }
    }
}

impl FromCst<Rc<FromClauseContextAll<'static>>> for FromClause {
    fn from_cst_with_context(
        cst: Rc<FromClauseContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        if let Some(table_alias_ctx) = cst.tableAlias() {
            let alias = table_alias_ctx
                .simpleIdentifier()
                .map(|x| ParsedSimpleIdentifier::from_cst_with_context(x.as_ref(), ctx))
                .unwrap_or_else(|| {
                    ctx.error("missing alias identifier")
                        .at(cst.as_ref())
                        .emit()
                        .into()
                });

            let table_ref_ctx = table_alias_ctx.tableReference();
            let identifier = table_ref_ctx
                .as_ref()
                .and_then(|tr| tr.identifier())
                .map(|id| ParsedIdentifier::from_cst_with_context(id, ctx))
                .unwrap_or_else(|| {
                    ctx.error("missing table reference")
                        .at(cst.as_ref())
                        .emit()
                        .into()
                });

            Rc::new(TableAlias {
                span: interval(table_alias_ctx.as_ref()).into(),
                alias,
                table: TableReference {
                    span: table_ref_ctx
                        .as_ref()
                        .map(|tr| interval(tr.as_ref()))
                        .into(),
                    identifier,
                },
            })
            .into()
        } else if let Some(table_ref_ctx) = cst.tableReference() {
            Rc::new(TableReference::from_cst_with_context(table_ref_ctx, ctx)).into()
        } else {
            Rc::new(TableReference {
                span: Span::NONE,
                identifier: ctx
                    .error("invalid from clause")
                    .at(cst.as_ref())
                    .emit()
                    .into(),
            })
            .into()
        }
    }
}

impl From<TableAlias> for FromClause {
    fn from(value: TableAlias) -> Self {
        Self::from(Rc::new(value))
    }
}

impl From<TableReference> for FromClause {
    fn from(value: TableReference) -> Self {
        Self::from(Rc::new(value))
    }
}

// ============================================================================
// SortExpression
// ============================================================================

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortOrder {
    Asc,
    Desc,
}

#[derive(Debug, Clone)]
pub struct SortExpression {
    pub span: Span,
    pub expression: Rc<Expression>,
    pub order: Option<SortOrder>,
}

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

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

impl FromCst<Rc<SortExpressionContextAll<'static>>> for SortExpression {
    fn from_cst_with_context(
        cst: Rc<SortExpressionContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        let expression = if let Some(expr_ctx) = cst.expression() {
            Expression::from_cst_with_context(expr_ctx.clone(), ctx)
        } else {
            let err = ctx
                .error("missing expression in sort")
                .at(cst.as_ref())
                .emit();
            Expression::from_kind(ErrorExpression { error: err.clone() })
        };

        let order = if cst.ASC().is_some() {
            Some(SortOrder::Asc)
        } else if cst.DESC().is_some() {
            Some(SortOrder::Desc)
        } else {
            None
        };

        Self {
            span: interval(cst.as_ref()).into(),
            expression: Rc::new(expression),
            order,
        }
    }
}

// ============================================================================
// Selection
// ============================================================================

#[derive(Debug, Clone)]
pub struct Selection {
    pub span: Span,
    pub identifier: ParsedIdentifier,
}

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

impl Selection {
    pub fn from_identifier(identifier: ParsedIdentifier) -> Self {
        Self {
            span: Span::NONE,
            identifier,
        }
    }
}

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

impl FromCst<Rc<SelectionContextAll<'static>>> for Selection {
    fn from_cst_with_context(
        cst: Rc<SelectionContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        let identifier = cst
            .identifier()
            .map(|id| ParsedIdentifier::from_cst_with_context(id, ctx))
            .unwrap_or_else(|| {
                ctx.error("missing identifier in selection")
                    .at(cst.as_ref())
                    .emit()
                    .into()
            });

        Self {
            span: interval(cst.as_ref()).into(),
            identifier,
        }
    }
}
