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

use super::node::Span;

use antlr_rust::tree::ParseTree;
use derive_more::derive::From;

use crate::{
    antlr::{
        hamelinparser::{
            ExactlyContextAttrs, NestedContext, NestedContextAttrs, PatternContextAll,
            QuantifiedContext, QuantifiedContextAttrs, QuantifierContextAll,
        },
        interval,
    },
    err::TranslationError,
    tree::ast::context::{FromCst, ParseContext},
};

use super::clause::FromClause;
use super::node::Spannable;

// ============================================================================
// Pattern - Top-level pattern type for MATCH command
// ============================================================================

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

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

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

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

impl FromCst<Rc<PatternContextAll<'static>>> for Pattern {
    fn from_cst_with_context(cst: Rc<PatternContextAll<'static>>, ctx: &mut ParseContext) -> Self {
        let kind = PatternKind::from_cst_with_context(Rc::clone(&cst), ctx);
        Self {
            span: interval(cst.as_ref()).into(),
            kind,
        }
    }
}

#[derive(Debug, Clone, From, PartialEq)]
pub enum PatternKind {
    Quantified(QuantifiedPattern),
    Nested(NestedPattern),
    Error(Rc<TranslationError>),
}

impl Spannable for PatternKind {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        match self {
            PatternKind::Quantified(p) => p.span(),
            PatternKind::Nested(p) => p.span(),
            PatternKind::Error(_) => None,
        }
    }
}

impl FromCst<Rc<PatternContextAll<'static>>> for PatternKind {
    fn from_cst_with_context(cst: Rc<PatternContextAll<'static>>, ctx: &mut ParseContext) -> Self {
        match cst.as_ref() {
            PatternContextAll::QuantifiedContext(qctx) => {
                QuantifiedPattern::from_ast_with_context(qctx, ctx).into()
            }
            PatternContextAll::NestedContext(nctx) => {
                NestedPattern::from_ast_with_context(nctx, ctx).into()
            }
            PatternContextAll::Error(_) => ctx.error("parse error").at(cst.as_ref()).emit().into(),
        }
    }
}

// ============================================================================
// QuantifiedPattern - A from clause with optional quantifier
// ============================================================================

#[derive(Debug, Clone)]
pub struct QuantifiedPattern {
    pub span: Span,
    pub from_clause: Rc<FromClause>,
    pub quantifier: Option<Rc<Quantifier>>,
}

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

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

impl QuantifiedPattern {
    fn from_ast_with_context(qctx: &QuantifiedContext<'static>, ctx: &mut ParseContext) -> Self {
        use super::clause::TableReference;

        let from_clause = Rc::new(
            qctx.fromClause()
                .map(|fc| FromClause::from_cst_with_context(fc, ctx))
                .unwrap_or_else(|| {
                    TableReference {
                        span: Span::NONE,
                        identifier: ctx
                            .error("missing from clause in quantified pattern")
                            .at(qctx)
                            .emit()
                            .into(),
                    }
                    .into()
                }),
        );

        let quantifier = qctx
            .quantifier()
            .map(|q| Rc::new(Quantifier::from_cst_with_context(q, ctx)));

        Self {
            span: interval(qctx).into(),
            from_clause,
            quantifier,
        }
    }
}

// ============================================================================
// NestedPattern - Nested patterns with quantifier (like ParenthesizedExpression)
// ============================================================================

#[derive(Debug, Clone)]
pub struct NestedPattern {
    pub span: Span,
    pub patterns: Vec<Rc<Pattern>>,
    pub quantifier: Rc<Quantifier>,
}

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

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

impl NestedPattern {
    fn from_ast_with_context(nctx: &NestedContext<'static>, ctx: &mut ParseContext) -> Self {
        let patterns = nctx
            .pattern_all()
            .into_iter()
            .map(|p| Rc::new(Pattern::from_cst_with_context(p, ctx)))
            .collect();

        let quantifier = Rc::new(
            nctx.quantifier()
                .map(|q| Quantifier::from_cst_with_context(q, ctx))
                .unwrap_or_else(|| {
                    ctx.error("missing quantifier in nested pattern")
                        .at(nctx)
                        .emit()
                        .into()
                }),
        );

        Self {
            span: interval(nctx).into(),
            patterns,
            quantifier,
        }
    }
}

// ============================================================================
// Quantifier - Pattern quantifiers (+, *, ?, {n})
// ============================================================================

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

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

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

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

impl From<Rc<TranslationError>> for Quantifier {
    fn from(error: Rc<TranslationError>) -> Self {
        Self {
            span: Span::NONE,
            kind: QuantifierKind::Error(error),
        }
    }
}

impl FromCst<Rc<QuantifierContextAll<'static>>> for Quantifier {
    fn from_cst_with_context(
        cst: Rc<QuantifierContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        let kind = QuantifierKind::from_cst_with_context(Rc::clone(&cst), ctx);
        Self {
            span: interval(cst.as_ref()).into(),
            kind,
        }
    }
}

#[derive(Debug, Clone, From, PartialEq)]
pub enum QuantifierKind {
    AtLeastOne,      // +
    AnyNumber,       // *
    ZeroOrOne,       // ?
    Exactly(String), // {n}
    Error(Rc<TranslationError>),
}

impl FromCst<Rc<QuantifierContextAll<'static>>> for QuantifierKind {
    fn from_cst_with_context(
        cst: Rc<QuantifierContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        match cst.as_ref() {
            QuantifierContextAll::AtLeastOneContext(_) => QuantifierKind::AtLeastOne,
            QuantifierContextAll::AnyNumberContext(_) => QuantifierKind::AnyNumber,
            QuantifierContextAll::ZeroOrOneContext(_) => QuantifierKind::ZeroOrOne,
            QuantifierContextAll::ExactlyContext(ectx) => match ectx.INTEGER_VALUE() {
                Some(v) => QuantifierKind::Exactly(v.get_text()),
                None => ctx
                    .error("missing integer value in exact quantifier")
                    .at(ectx)
                    .emit()
                    .into(),
            },
            QuantifierContextAll::Error(_) => {
                ctx.error("parse error").at(cst.as_ref()).emit().into()
            }
        }
    }
}
