use std::{
    collections::{HashMap, HashSet},
    fmt::{self, Display, Formatter},
    ops::RangeInclusive,
    rc::Rc,
    sync::Arc,
};

use ordermap::OrderSet;

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

use crate::antlr::strip_numeric_separators;

use super::clause::{FromClause, TableReference};
use super::dataset_identifier::QualifiedDatasetIdentifier;
use super::identifier::{Identifier, SimpleIdentifier};
use super::node::{Span, Spannable};
use super::query::push_datasets_for_table_reference;
use crate::tree::options::TemplateParameterKind;
use crate::{
    antlr::{
        hamelinparser::{
            ExactlyContextAttrs, NestedContext, NestedContextAttrs, PatternContextAll,
            QuantifiedContext, QuantifiedContextAttrs, QuantifierContextAll,
        },
        interval,
    },
    err::TranslationError,
    tree::ast::context::{FromCst, ParseContext},
};

// ============================================================================
// 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 Pattern {
    pub fn has_template_parameters(&self) -> bool {
        match &self.kind {
            PatternKind::Quantified(qp) => qp.from_clause.table_reference().is_templated(),
            PatternKind::Nested(np) => np.patterns.iter().any(|p| p.has_template_parameters()),
            PatternKind::Error(_) => false,
        }
    }

    /// Recursively extracts table references from this pattern, excluding CTEs and duplicates.
    /// Templated-vs-static handling is delegated to
    /// [`push_datasets_for_table_reference`](super::query::push_datasets_for_table_reference)
    /// so MATCH patterns and top-level FROM/UNION/JOIN clauses agree on expansion behavior and
    /// error messages.
    pub fn extract_datasets(
        &self,
        cte_names: &HashSet<Identifier>,
        datasets: &mut OrderSet<QualifiedDatasetIdentifier>,
        default_space: Option<&SimpleIdentifier>,
        template_parameters: Option<&HashMap<String, TemplateParameterKind>>,
    ) -> Result<(), Arc<TranslationError>> {
        match &self.kind {
            PatternKind::Quantified(qp) => {
                let table_ref = qp.from_clause.table_reference();
                push_datasets_for_table_reference(
                    &table_ref,
                    cte_names,
                    datasets,
                    default_space,
                    template_parameters,
                )?;
            }
            PatternKind::Nested(np) => {
                for pattern in &np.patterns {
                    pattern.extract_datasets(
                        cte_names,
                        datasets,
                        default_space,
                        template_parameters,
                    )?;
                }
            }
            PatternKind::Error(_) => {}
        }
        Ok(())
    }
}

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(Arc<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: Arc<FromClause>,
    pub quantifier: Option<Arc<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 {
        let from_clause = Arc::new(
            qctx.fromClause()
                .map(|fc| FromClause::from_cst_with_context(fc, ctx))
                .unwrap_or_else(|| {
                    TableReference {
                        span: Span::NONE,
                        target: ctx
                            .error("missing from clause in quantified pattern")
                            .at(qctx)
                            .emit()
                            .into(),
                    }
                    .into()
                }),
        );

        let quantifier = qctx
            .quantifier()
            .map(|q| Arc::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<Arc<Pattern>>,
    pub quantifier: Arc<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| Arc::new(Pattern::from_cst_with_context(p, ctx)))
            .collect();

        let quantifier = Arc::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<Arc<TranslationError>> for Quantifier {
    fn from(error: Arc<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(Arc<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(strip_numeric_separators(&v.get_text()).into_owned())
                }
                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()
            }
        }
    }
}

// ============================================================================
// Display / fmt_indented implementations
// ============================================================================

impl Pattern {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.kind.fmt_indented(f, indentation)
    }
}

impl Display for Pattern {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.fmt_indented(f, 0)
    }
}

impl PatternKind {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        match self {
            PatternKind::Quantified(q) => q.fmt_indented(f, indentation),
            PatternKind::Nested(n) => n.fmt_indented(f, indentation),
            PatternKind::Error(_) => write!(f, "?!"),
        }
    }
}

impl QuantifiedPattern {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.from_clause.fmt_indented(f, indentation)?;
        if let Some(q) = &self.quantifier {
            write!(f, "{}", q)?;
        }
        Ok(())
    }
}

impl NestedPattern {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        write!(f, "(")?;
        for (i, pat) in self.patterns.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            pat.fmt_indented(f, indentation + 1)?;
        }
        write!(f, ")")?;
        write!(f, "{}", self.quantifier)
    }
}

impl Display for Quantifier {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}

impl Display for QuantifierKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            QuantifierKind::AtLeastOne => write!(f, "+"),
            QuantifierKind::AnyNumber => write!(f, "*"),
            QuantifierKind::ZeroOrOne => write!(f, "?"),
            QuantifierKind::Exactly(n) => write!(f, "{{{}}}", n),
            QuantifierKind::Error(_) => write!(f, "?!"),
        }
    }
}
