use std::rc::Rc;

use derive_more::derive::From;

use crate::{
    err::TranslationError,
    sql::expression::identifier::Identifier as SqlIdentifier,
    tree::ast::{
        node::Span,
        pattern::{
            NestedPattern, Pattern, PatternKind, QuantifiedPattern, Quantifier, QuantifierKind,
        },
    },
    types::struct_type::Struct,
};

use super::{
    clause::TypedFromClause, context::StatementTranslationContext, environment::TypeEnvironment,
};

// ============================================================================
// TypedPattern - A regex-style pattern whose variables are resolved datasets
// ============================================================================

/// A type-checked pattern for MATCH commands
///
/// Patterns are NodeClause types - the variants (Quantified/Nested) store AST references
/// for span information. They are embedded parts of the MATCH command, not standalone nodes.
#[derive(Debug, Clone, From)]
pub enum TypedPattern {
    /// A dataset reference with optional quantifier
    Quantified(TypedQuantifiedPattern),

    /// Nested patterns with required quantifier
    Nested(TypedNestedPattern),

    /// Pattern type-checking error
    Error(Rc<TranslationError>),
}

impl TypedPattern {
    /// Type-check a pattern from AST
    pub fn from_ast_with_context(
        pattern: Rc<Pattern>,
        ctx: &mut StatementTranslationContext,
    ) -> Self {
        match &pattern.kind {
            PatternKind::Quantified(quant_pattern) => {
                // Clone to get Rc<QuantifiedPattern>
                let ast = Rc::new(quant_pattern.clone());

                // Resolve the dataset schema from the FROM clause
                let typed_from =
                    TypedFromClause::from_ast_with_context(ast.from_clause.clone(), ctx);

                // Resolve quantifier - if None in AST, default to Exactly("1")
                let quantifier = ast.quantifier.clone().unwrap_or_else(|| {
                    Rc::new(Quantifier {
                        span: Span::NONE,
                        kind: QuantifierKind::Exactly("1".to_string()),
                    })
                });

                TypedQuantifiedPattern {
                    ast,
                    typed_from,
                    quantifier,
                }
                .into()
            }
            PatternKind::Nested(nested_pattern) => {
                // Clone to get Rc<NestedPattern>
                let ast = Rc::new(nested_pattern.clone());

                // Recursively type-check nested patterns
                let typed_patterns = ast
                    .patterns
                    .iter()
                    .map(|p| TypedPattern::from_ast_with_context(p.clone(), ctx))
                    .collect();

                TypedNestedPattern {
                    ast,
                    patterns: typed_patterns,
                    quantifier: nested_pattern.quantifier.clone(),
                }
                .into()
            }
            PatternKind::Error(err) => err.clone().into(),
        }
    }

    /// Convert back to untyped AST pattern
    pub fn to_ast(&self) -> Rc<Pattern> {
        match self {
            TypedPattern::Quantified(quant) => Rc::new(Pattern {
                span: quant.ast.span.clone(),
                kind: quant.ast.as_ref().clone().into(),
            }),
            TypedPattern::Nested(nested) => Rc::new(Pattern {
                span: nested.ast.span.clone(),
                kind: NestedPattern {
                    span: nested.ast.span.clone(),
                    patterns: nested.patterns.iter().map(|p| p.to_ast()).collect(),
                    quantifier: nested.quantifier.clone(),
                }
                .into(),
            }),
            TypedPattern::Error(err) => Rc::new(Pattern {
                span: Span::NONE,
                kind: err.clone().into(),
            }),
        }
    }

    /// Collect all pattern bindings from this pattern (recursively for nested patterns)
    ///
    /// Returns a Struct with pattern names as field names, mapping to their schemas.
    /// - For explicit aliases (FROM x = events), the binding name is `x`
    /// - For table references (FROM events), the binding name is `events`
    pub fn collect_pattern_bindings(&self) -> Struct {
        match self {
            TypedPattern::Quantified(quant) => {
                let mut bindings = Struct::default();
                match &quant.typed_from {
                    TypedFromClause::Alias(alias_clause) => {
                        // Use the explicit alias as the binding name
                        if let Ok(alias) = alias_clause.alias.clone().valid() {
                            let sql_ident = SqlIdentifier::from(alias);
                            bindings.insert(
                                sql_ident,
                                alias_clause.resolved.environment().flatten().into(),
                            );
                        }
                    }
                    TypedFromClause::Reference(table_ref) => {
                        // Use the table name as the binding name
                        // Extract valid identifier (skip parse errors)
                        if let Ok(identifier) = table_ref.ast.identifier.clone().valid() {
                            let sql_ident = SqlIdentifier::from(identifier.clone());
                            bindings.insert(
                                sql_ident,
                                table_ref.resolved.environment().flatten().into(),
                            );
                        }
                    }
                    TypedFromClause::Error(_) => {
                        // Skip error patterns
                    }
                }
                bindings
            }
            TypedPattern::Nested(nested) => {
                // Recursively collect bindings from all nested patterns
                let mut bindings = Struct::default();
                for pattern in &nested.patterns {
                    let pattern_bindings = pattern.collect_pattern_bindings();
                    // Merge nested bindings into the accumulated bindings
                    for (name, typ) in pattern_bindings.fields.into_iter() {
                        bindings.insert(name.into(), typ);
                    }
                }
                bindings
            }
            TypedPattern::Error(_) => Struct::default(),
        }
    }
}

// ============================================================================
// TypedQuantifiedPattern - A dataset with optional quantifier
// ============================================================================

/// A type-checked quantified pattern (dataset with resolved quantifier)
#[derive(Debug, Clone)]
pub struct TypedQuantifiedPattern {
    /// Reference to the AST pattern
    pub ast: Rc<QuantifiedPattern>,

    /// The typed FROM clause (contains resolved environment and alias)
    pub typed_from: TypedFromClause,

    /// The resolved quantifier (always present - defaults to Exactly("1") if not specified in AST)
    pub quantifier: Rc<Quantifier>,
}

impl TypedQuantifiedPattern {
    /// Get the environment for this pattern
    pub fn environment(&self) -> Rc<TypeEnvironment> {
        self.typed_from.environment()
    }

    /// Get the pattern name (EXPLICIT alias only)
    /// Returns None for patterns without explicit aliases (FROM events)
    /// Returns Some for patterns with explicit aliases (FROM x = events)
    pub fn pattern_name(&self) -> Option<&crate::tree::ast::identifier::ParsedSimpleIdentifier> {
        self.typed_from.alias()
    }
}

// ============================================================================
// TypedNestedPattern - Grouped patterns with quantifier
// ============================================================================

/// A type-checked nested pattern (grouped patterns with quantifier)
#[derive(Debug, Clone)]
pub struct TypedNestedPattern {
    /// Reference to the AST pattern
    pub ast: Rc<NestedPattern>,

    /// The nested patterns
    pub patterns: Vec<TypedPattern>,

    /// The quantifier (always present for nested patterns in AST)
    pub quantifier: Rc<Quantifier>,
}
