//! Abstract syntax tree definitions for BHC.
//!
//! This crate defines the AST produced by parsing Haskell 2026 source code.
//! The AST preserves source locations and syntactic structure.

#![warn(missing_docs)]

use bhc_index::define_index;
use bhc_intern::{Ident, Symbol};
use bhc_span::Span;

define_index! {
    /// Index into the expression arena.
    pub struct ExprId;

    /// Index into the pattern arena.
    pub struct PatId;

    /// Index into the type arena.
    pub struct TypeId;

    /// Index into the declaration arena.
    pub struct DeclId;
}

// ============================================================
// Documentation Comments
// ============================================================

/// The kind of documentation comment.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DocKind {
    /// Preceding documentation: `-- |` or `{- | -}`
    /// Documents the following item.
    Preceding,
    /// Trailing documentation: `-- ^` or `{- ^ -}`
    /// Documents the preceding item.
    Trailing,
}

/// A Haddock-style documentation comment.
#[derive(Clone, Debug)]
pub struct DocComment {
    /// The raw text content of the documentation.
    pub text: String,
    /// The kind of documentation (preceding or trailing).
    pub kind: DocKind,
    /// The span of the comment in source.
    pub span: Span,
}

impl DocComment {
    /// Create a new preceding documentation comment.
    #[must_use]
    pub fn preceding(text: String, span: Span) -> Self {
        Self {
            text,
            kind: DocKind::Preceding,
            span,
        }
    }

    /// Create a new trailing documentation comment.
    #[must_use]
    pub fn trailing(text: String, span: Span) -> Self {
        Self {
            text,
            kind: DocKind::Trailing,
            span,
        }
    }
}

/// A Haskell module.
#[derive(Clone, Debug)]
pub struct Module {
    /// Module documentation comment.
    pub doc: Option<DocComment>,
    /// Module pragmas (LANGUAGE, OPTIONS_GHC, etc.).
    pub pragmas: Vec<Pragma>,
    /// Module name.
    pub name: Option<ModuleName>,
    /// Export list.
    pub exports: Option<Vec<Export>>,
    /// Import declarations.
    pub imports: Vec<ImportDecl>,
    /// Top-level declarations.
    pub decls: Vec<Decl>,
    /// Span of the entire module.
    pub span: Span,
}

// ============================================================
// Pragmas
// ============================================================

/// A pragma in the source code.
#[derive(Clone, Debug)]
pub struct Pragma {
    /// The kind of pragma.
    pub kind: PragmaKind,
    /// The span.
    pub span: Span,
}

/// The kind of pragma.
#[derive(Clone, Debug)]
pub enum PragmaKind {
    /// Language extension: `{-# LANGUAGE GADTs #-}`
    Language(Vec<Symbol>),
    /// GHC options: `{-# OPTIONS_GHC -Wall #-}`
    OptionsGhc(String),
    /// Inline pragma: `{-# INLINE foo #-}`
    Inline(Ident),
    /// No-inline pragma: `{-# NOINLINE foo #-}`
    NoInline(Ident),
    /// Inlinable pragma: `{-# INLINABLE foo #-}`
    Inlinable(Ident),
    /// Specialize pragma: `{-# SPECIALIZE foo :: Int -> Int #-}`
    Specialize(Ident, Type),
    /// Unpack pragma: `{-# UNPACK #-}`
    Unpack,
    /// No-unpack pragma: `{-# NOUNPACK #-}`
    NoUnpack,
    /// Source pragma (for generated code): `{-# SOURCE #-}`
    Source,
    /// Complete pragma: `{-# COMPLETE Pat1, Pat2 #-}`
    Complete(Vec<Ident>),
    /// Minimal pragma: `{-# MINIMAL foo | bar #-}`
    Minimal(String),
    /// Deprecated pragma: `{-# DEPRECATED foo "message" #-}`
    Deprecated(Option<Vec<Ident>>, String),
    /// Warning pragma: `{-# WARNING foo "message" #-}`
    Warning(Option<Vec<Ident>>, String),
    /// Unknown/unsupported pragma (preserved for compatibility)
    Other(String),
}

/// Known language extensions.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Extension {
    // Type system
    /// GADTs
    GADTs,
    /// Type families
    TypeFamilies,
    /// Data kinds
    DataKinds,
    /// Kind signatures
    KindSignatures,
    /// Rank-N types
    RankNTypes,
    /// Existential quantification
    ExistentialQuantification,
    /// Scoped type variables
    ScopedTypeVariables,
    /// Type applications
    TypeApplications,
    /// Flexible instances
    FlexibleInstances,
    /// Flexible contexts
    FlexibleContexts,
    /// Multi-param type classes
    MultiParamTypeClasses,
    /// Functional dependencies
    FunctionalDependencies,
    /// Undecidable instances
    UndecidableInstances,
    /// Overlapping instances
    OverlappingInstances,
    /// Constraint kinds
    ConstraintKinds,

    // Syntax
    /// Lambda case
    LambdaCase,
    /// Multi-way if
    MultiWayIf,
    /// Block arguments
    BlockArguments,
    /// Pattern guards
    PatternGuards,
    /// View patterns
    ViewPatterns,
    /// Pattern synonyms
    PatternSynonyms,
    /// Record wild cards
    RecordWildCards,
    /// Named field puns
    NamedFieldPuns,
    /// Overloaded strings
    OverloadedStrings,
    /// Tuple sections
    TupleSections,
    /// Overloaded lists
    OverloadedLists,
    /// Numeric underscores
    NumericUnderscores,
    /// Hex float literals
    HexFloatLiterals,
    /// Binary literals
    BinaryLiterals,
    /// Negative literals
    NegativeLiterals,

    // Strictness
    /// Bang patterns
    BangPatterns,
    /// Strict data
    StrictData,
    /// Strict
    Strict,

    // Deriving
    /// Derive functor
    DeriveFunctor,
    /// Derive foldable
    DeriveFoldable,
    /// Derive traversable
    DeriveTraversable,
    /// Derive generic
    DeriveGeneric,
    /// Derive data typeable
    DeriveDataTypeable,
    /// Derive lift
    DeriveLift,
    /// Deriving via
    DerivingVia,
    /// Deriving strategies
    DerivingStrategies,
    /// Generalized newtype deriving
    GeneralizedNewtypeDeriving,
    /// Standalone deriving
    StandaloneDeriving,

    // FFI
    /// Foreign function interface
    ForeignFunctionInterface,
    /// C API FFI
    CApiFFI,
    /// Unsafe FFI
    UnliftedFFITypes,

    // Other
    /// Template Haskell
    TemplateHaskell,
    /// Template Haskell quotes
    TemplateHaskellQuotes,
    /// Quasi quotes
    QuasiQuotes,
    /// Type operators
    TypeOperators,
    /// Explicit forall
    ExplicitForAll,
    /// Explicit namespaces
    ExplicitNamespaces,
    /// Empty data declarations
    EmptyDataDecls,
    /// Empty case
    EmptyCase,
    /// Instance sigs
    InstanceSigs,
    /// Default signatures
    DefaultSignatures,
    /// Named defaults
    NamedDefaults,
    /// C preprocessor
    CPP,

    /// Unknown extension (preserved)
    Unknown(Symbol),
}

/// Classification of extension support status.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExtensionStatus {
    /// Fully implemented or always-on in BHC.
    Supported,
    /// Recognized but not yet implemented — emits a warning.
    Unimplemented,
    /// Unknown extension name.
    Unknown,
}

impl Extension {
    /// Classify this extension's support status in BHC.
    #[must_use]
    pub fn status(&self) -> ExtensionStatus {
        match self {
            // Type system — implemented or always-on
            Self::GADTs
            | Self::TypeFamilies
            | Self::DataKinds
            | Self::KindSignatures
            | Self::ScopedTypeVariables
            | Self::TypeApplications
            | Self::FlexibleInstances
            | Self::FlexibleContexts
            | Self::MultiParamTypeClasses
            | Self::FunctionalDependencies
            | Self::TypeOperators
            | Self::ExplicitForAll => ExtensionStatus::Supported,

            // Syntax — always-on in BHC
            Self::LambdaCase
            | Self::MultiWayIf
            | Self::BlockArguments
            | Self::PatternGuards
            | Self::ViewPatterns
            | Self::PatternSynonyms
            | Self::RecordWildCards
            | Self::NamedFieldPuns
            | Self::OverloadedStrings
            | Self::TupleSections
            | Self::OverloadedLists
            | Self::NumericUnderscores
            | Self::BinaryLiterals
            | Self::NegativeLiterals => ExtensionStatus::Supported,

            // Strictness — always-on or handled
            Self::BangPatterns | Self::StrictData | Self::Strict => ExtensionStatus::Supported,

            // Deriving — implemented
            Self::DeriveFunctor
            | Self::DeriveFoldable
            | Self::DeriveTraversable
            | Self::DeriveGeneric
            | Self::DerivingVia
            | Self::DerivingStrategies
            | Self::GeneralizedNewtypeDeriving
            | Self::StandaloneDeriving
            | Self::DeriveDataTypeable => ExtensionStatus::Supported,

            // FFI — implemented
            Self::ForeignFunctionInterface => ExtensionStatus::Supported,

            // Other — implemented
            Self::EmptyDataDecls
            | Self::EmptyCase
            | Self::InstanceSigs
            | Self::DefaultSignatures
            | Self::ExplicitNamespaces
            | Self::CPP => ExtensionStatus::Supported,

            // Extensions that are accepted for compatibility but silently
            // relaxed (BHC doesn't enforce the restriction they lift)
            Self::UndecidableInstances | Self::OverlappingInstances => ExtensionStatus::Supported,

            // Existential quantification in data constructors
            Self::ExistentialQuantification => ExtensionStatus::Supported,

            // Rank-N polymorphism (forall in function argument positions)
            Self::RankNTypes => ExtensionStatus::Supported,

            // Recognized but not yet implemented
            Self::ConstraintKinds
            | Self::HexFloatLiterals
            | Self::DeriveLift
            | Self::CApiFFI
            | Self::UnliftedFFITypes
            | Self::TemplateHaskell
            | Self::TemplateHaskellQuotes
            | Self::QuasiQuotes
            | Self::NamedDefaults => ExtensionStatus::Unimplemented,

            // Unknown — check if it's a recognized GHC extension we silently accept
            Self::Unknown(name) => {
                match name.as_str() {
                    // Common GHC extensions we silently accept for compatibility
                    "Arrows"
                    | "MonoLocalBinds"
                    | "ImpredicativeTypes"
                    | "TypeFamilyDependencies"
                    | "DeriveAnyClass"
                    | "RoleAnnotations"
                    | "AllowAmbiguousTypes"
                    | "TypeSynonymInstances"
                    | "PackageImports"
                    | "NoImplicitPrelude"
                    | "NoMonomorphismRestriction"
                    | "DisambiguateRecordFields"
                    | "DuplicateRecordFields"
                    | "ApplicativeDo"
                    | "NumDecimals"
                    | "MagicHash"
                    | "UnboxedTuples"
                    | "TypeInType"
                    | "PolyKinds"
                    | "StarIsType"
                    | "ImportQualifiedPost"
                    | "StandaloneKindSignatures"
                    | "QuantifiedConstraints"
                    | "LinearTypes"
                    | "UnicodeSyntax"
                    | "ParallelListComp"
                    | "TransformListComp"
                    | "MonadComprehensions"
                    | "ExtendedDefaultRules"
                    | "PostfixOperators"
                    | "ScopedTypeVariables"
                    | "NoFieldSelectors" => ExtensionStatus::Supported,
                    _ => ExtensionStatus::Unknown,
                }
            }
        }
    }

    /// Parse an extension name.
    #[must_use]
    pub fn from_name(name: &str) -> Self {
        match name {
            // Type system
            "GADTs" => Self::GADTs,
            "TypeFamilies" => Self::TypeFamilies,
            "DataKinds" => Self::DataKinds,
            "KindSignatures" => Self::KindSignatures,
            "RankNTypes" | "Rank2Types" | "PolymorphicComponents" => Self::RankNTypes,
            "ExistentialQuantification" => Self::ExistentialQuantification,
            "ScopedTypeVariables" => Self::ScopedTypeVariables,
            "TypeApplications" => Self::TypeApplications,
            "FlexibleInstances" => Self::FlexibleInstances,
            "FlexibleContexts" => Self::FlexibleContexts,
            "MultiParamTypeClasses" => Self::MultiParamTypeClasses,
            "FunctionalDependencies" => Self::FunctionalDependencies,
            "UndecidableInstances" => Self::UndecidableInstances,
            "OverlappingInstances" | "IncoherentInstances" => Self::OverlappingInstances,
            "ConstraintKinds" => Self::ConstraintKinds,

            // Syntax
            "LambdaCase" => Self::LambdaCase,
            "MultiWayIf" => Self::MultiWayIf,
            "BlockArguments" => Self::BlockArguments,
            "PatternGuards" => Self::PatternGuards,
            "ViewPatterns" => Self::ViewPatterns,
            "PatternSynonyms" => Self::PatternSynonyms,
            "RecordWildCards" => Self::RecordWildCards,
            "NamedFieldPuns" => Self::NamedFieldPuns,
            "OverloadedStrings" => Self::OverloadedStrings,
            "TupleSections" => Self::TupleSections,
            "OverloadedLists" => Self::OverloadedLists,
            "NumericUnderscores" => Self::NumericUnderscores,
            "HexFloatLiterals" => Self::HexFloatLiterals,
            "BinaryLiterals" => Self::BinaryLiterals,
            "NegativeLiterals" => Self::NegativeLiterals,

            // Strictness
            "BangPatterns" => Self::BangPatterns,
            "StrictData" => Self::StrictData,
            "Strict" => Self::Strict,

            // Deriving
            "DeriveFunctor" => Self::DeriveFunctor,
            "DeriveFoldable" => Self::DeriveFoldable,
            "DeriveTraversable" => Self::DeriveTraversable,
            "DeriveGeneric" => Self::DeriveGeneric,
            "DeriveDataTypeable" => Self::DeriveDataTypeable,
            "DeriveLift" => Self::DeriveLift,
            "DerivingVia" => Self::DerivingVia,
            "DerivingStrategies" => Self::DerivingStrategies,
            "GeneralizedNewtypeDeriving" | "GeneralisedNewtypeDeriving" => {
                Self::GeneralizedNewtypeDeriving
            }
            "StandaloneDeriving" => Self::StandaloneDeriving,

            // FFI
            "ForeignFunctionInterface" | "FFI" => Self::ForeignFunctionInterface,
            "CApiFFI" => Self::CApiFFI,
            "UnliftedFFITypes" => Self::UnliftedFFITypes,

            // Other
            "TemplateHaskell" => Self::TemplateHaskell,
            "TemplateHaskellQuotes" => Self::TemplateHaskellQuotes,
            "QuasiQuotes" => Self::QuasiQuotes,
            "TypeOperators" => Self::TypeOperators,
            "ExplicitForAll" => Self::ExplicitForAll,
            "ExplicitNamespaces" => Self::ExplicitNamespaces,
            "EmptyDataDecls" => Self::EmptyDataDecls,
            "EmptyCase" => Self::EmptyCase,
            "InstanceSigs" => Self::InstanceSigs,
            "DefaultSignatures" => Self::DefaultSignatures,
            "NamedDefaults" => Self::NamedDefaults,
            "CPP" => Self::CPP,

            _ => Self::Unknown(Symbol::intern(name)),
        }
    }
}

/// A qualified module name like `Data.List`.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ModuleName {
    /// The components of the name.
    pub parts: Vec<Symbol>,
    /// The span.
    pub span: Span,
}

impl std::fmt::Display for ModuleName {
    /// Get the fully qualified name as a dotted string (e.g. `Data.Map`).
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let joined = self
            .parts
            .iter()
            .map(|s| s.as_str())
            .collect::<Vec<_>>()
            .join(".");
        f.write_str(&joined)
    }
}

/// An export specification.
#[derive(Clone, Debug)]
pub enum Export {
    /// Export a value: `foo`
    Var(Ident, Span),
    /// Export a type with optional constructors: `Foo(..)` or `Foo(A, B)`
    Type(Ident, Option<Vec<Ident>>, Span),
    /// Export a module: `module Data.List`
    Module(ModuleName, Span),
    /// Export a pattern synonym: `pattern Foo`
    Pattern(Ident, Span),
}

/// An import declaration.
#[derive(Clone, Debug)]
pub struct ImportDecl {
    /// The module being imported.
    pub module: ModuleName,
    /// Whether this is a qualified import.
    pub qualified: bool,
    /// The alias for qualified imports.
    pub alias: Option<ModuleName>,
    /// The import specification (hiding or explicit).
    pub spec: Option<ImportSpec>,
    /// The span.
    pub span: Span,
}

/// Import specification.
#[derive(Clone, Debug)]
pub enum ImportSpec {
    /// Import only the listed items.
    Only(Vec<Import>),
    /// Import everything except the listed items.
    Hiding(Vec<Import>),
}

/// A single import item.
#[derive(Clone, Debug)]
pub enum Import {
    /// Import a value.
    Var(Ident, Span),
    /// Import a type with optional constructors.
    Type(Ident, Option<Vec<Ident>>, Span),
    /// Import a pattern synonym: `pattern Foo`
    Pattern(Ident, Span),
}

/// A top-level declaration.
#[derive(Clone, Debug)]
pub enum Decl {
    /// Type signature: `foo :: Int -> Int`
    TypeSig(TypeSig),
    /// Function/value binding: `foo x = x + 1`
    FunBind(FunBind),
    /// Data type: `data Foo = A | B Int`
    DataDecl(DataDecl),
    /// Type alias: `type Foo = Bar`
    TypeAlias(TypeAlias),
    /// Newtype: `newtype Foo = Foo Bar`
    Newtype(NewtypeDecl),
    /// Class definition: `class Eq a where ...`
    ClassDecl(ClassDecl),
    /// Instance definition: `instance Eq Int where ...`
    InstanceDecl(InstanceDecl),
    /// Foreign import/export
    Foreign(ForeignDecl),
    /// Fixity declaration: `infixl 6 +`
    Fixity(FixityDecl),
    /// Pragma declaration: `{-# MINIMAL ... #-}`
    PragmaDecl(Pragma),
    /// Standalone deriving: `deriving instance Show Foo`
    StandaloneDeriving(StandaloneDeriving),
    /// Pattern synonym: `pattern Zero = Lit 0`
    PatternSynonym(PatternSynonymDecl),
    /// Standalone type family: `type family F a` or `type family F a where ...`
    TypeFamilyDecl(TypeFamilyDecl),
    /// Standalone type family instance: `type instance F Int = Bool`
    TypeInstanceDecl(TypeInstanceDecl),
    /// Standalone data family: `data family F a`
    DataFamilyDecl(DataFamilyDecl),
    /// Data family instance: `data instance F Int = Con1 Int | Con2`
    DataInstanceDecl(DataInstanceDecl),
}

/// A standalone deriving declaration: `deriving instance Show Foo`
#[derive(Clone, Debug)]
pub struct StandaloneDeriving {
    /// The class to derive (e.g., Show, Eq).
    pub class: Ident,
    /// The type to derive for (e.g., Foo).
    pub ty: Type,
    /// Source span.
    pub span: Span,
}

/// A pattern synonym declaration: `pattern Zero = Lit 0`
#[derive(Clone, Debug)]
pub struct PatternSynonymDecl {
    /// The pattern synonym name (e.g., Zero).
    pub name: Ident,
    /// Pattern variables.
    pub args: Vec<Ident>,
    /// Direction: bidirectional (`=`) or unidirectional (`<-`).
    pub direction: PatSynDir,
    /// The RHS pattern.
    pub pattern: Pat,
    /// Source span.
    pub span: Span,
}

/// Whether a type family is open or closed.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TypeFamilyKind {
    /// Open type family: `type family F a`
    Open,
    /// Closed type family: `type family F a where ...`
    Closed,
}

/// A standalone type family declaration.
///
/// Open:   `type family F a`
/// Closed: `type family F a where F Int = Bool; F a = ()`
#[derive(Clone, Debug)]
pub struct TypeFamilyDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The family name.
    pub name: Ident,
    /// Type parameters.
    pub params: Vec<TyVar>,
    /// Optional result kind signature.
    pub kind: Option<Kind>,
    /// Whether this family is open or closed.
    pub family_kind: TypeFamilyKind,
    /// Equations (only for closed families).
    pub equations: Vec<TypeFamilyEqn>,
    /// The span.
    pub span: Span,
}

/// A single equation in a closed type family.
#[derive(Clone, Debug)]
pub struct TypeFamilyEqn {
    /// Type argument patterns.
    pub args: Vec<Type>,
    /// The right-hand side type.
    pub rhs: Type,
    /// The span.
    pub span: Span,
}

/// A standalone type instance declaration (for open type families).
///
/// `type instance F Int = Bool`
#[derive(Clone, Debug)]
pub struct TypeInstanceDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The family name.
    pub name: Ident,
    /// Type argument patterns.
    pub args: Vec<Type>,
    /// The right-hand side type.
    pub rhs: Type,
    /// The span.
    pub span: Span,
}

/// A standalone data family declaration: `data family F a`
#[derive(Clone, Debug)]
pub struct DataFamilyDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The family name.
    pub name: Ident,
    /// Type parameters.
    pub params: Vec<TyVar>,
    /// Optional result kind signature.
    pub kind: Option<Kind>,
    /// The span.
    pub span: Span,
}

/// A data family instance: `data instance F Int = Con1 Int | Con2`
#[derive(Clone, Debug)]
pub struct DataInstanceDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The family name.
    pub family_name: Ident,
    /// Type argument patterns (e.g., `[Int]`).
    pub args: Vec<Type>,
    /// H98 constructors.
    pub constrs: Vec<ConDecl>,
    /// GADT constructors.
    pub gadt_constrs: Vec<GadtConDecl>,
    /// Deriving clauses.
    pub deriving: Vec<DerivingClause>,
    /// The span.
    pub span: Span,
}

/// Direction of a pattern synonym.
#[derive(Clone, Debug)]
pub enum PatSynDir {
    /// `pattern Foo x = Con x 0` — usable in both patterns and expressions.
    Bidirectional,
    /// `pattern Foo x <- Con x _` — usable only in patterns.
    Unidirectional,
}

/// A type signature.
#[derive(Clone, Debug)]
pub struct TypeSig {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The names being typed.
    pub names: Vec<Ident>,
    /// The type.
    pub ty: Type,
    /// The span.
    pub span: Span,
}

/// A function binding.
#[derive(Clone, Debug)]
pub struct FunBind {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The function name.
    pub name: Ident,
    /// The clauses (pattern matches).
    pub clauses: Vec<Clause>,
    /// The span.
    pub span: Span,
}

/// A clause in a function binding.
#[derive(Clone, Debug)]
pub struct Clause {
    /// The patterns for arguments.
    pub pats: Vec<Pat>,
    /// The right-hand side.
    pub rhs: Rhs,
    /// Local bindings.
    pub wheres: Vec<Decl>,
    /// The span.
    pub span: Span,
}

/// The right-hand side of a binding.
#[derive(Clone, Debug)]
pub enum Rhs {
    /// Simple: `= expr`
    Simple(Expr, Span),
    /// Guarded: `| guard = expr`
    Guarded(Vec<GuardedRhs>, Span),
}

/// A guarded right-hand side.
#[derive(Clone, Debug)]
pub struct GuardedRhs {
    /// The guards (can be multiple, e.g., `| pat <- expr, cond`).
    pub guards: Vec<Guard>,
    /// The body expression.
    pub body: Expr,
    /// The span.
    pub span: Span,
}

/// A guard in a guarded RHS.
#[derive(Clone, Debug)]
pub enum Guard {
    /// A pattern guard: `pat <- expr`
    Pattern(Pat, Expr, Span),
    /// A boolean guard: `expr`
    Expr(Expr, Span),
}

impl Guard {
    /// Get the span of this guard.
    #[must_use]
    pub fn span(&self) -> Span {
        match self {
            Self::Pattern(_, _, s) | Self::Expr(_, s) => *s,
        }
    }
}

/// A deriving strategy annotation.
#[derive(Clone, Debug)]
pub enum DerivingStrategy {
    /// No explicit strategy; use heuristic.
    Default,
    /// `stock` — use built-in derivation (Eq, Ord, Show, etc.).
    Stock,
    /// `newtype` — GeneralizedNewtypeDeriving.
    Newtype,
    /// `anyclass` — DeriveAnyClass (empty instance with defaults).
    Anyclass,
    /// `via SomeType` — DerivingVia.
    Via(Type),
}

/// A single deriving clause entry: a strategy paired with a class name.
#[derive(Clone, Debug)]
pub struct DerivingClause {
    /// The deriving strategy.
    pub strategy: DerivingStrategy,
    /// The class to derive.
    pub class: Ident,
}

/// A data type declaration.
#[derive(Clone, Debug)]
pub struct DataDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The type name.
    pub name: Ident,
    /// Type parameters.
    pub params: Vec<TyVar>,
    /// Constructors (H98 syntax).
    pub constrs: Vec<ConDecl>,
    /// GADT constructors (where syntax).
    pub gadt_constrs: Vec<GadtConDecl>,
    /// Deriving clauses.
    pub deriving: Vec<DerivingClause>,
    /// The span.
    pub span: Span,
}

/// A GADT constructor declaration: `ConName :: Type`.
#[derive(Clone, Debug)]
pub struct GadtConDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// Constructor name.
    pub name: Ident,
    /// The full constructor type, e.g. `Int -> Expr Int`.
    pub ty: Type,
    /// The span.
    pub span: Span,
}

/// A data constructor declaration.
#[derive(Clone, Debug)]
pub struct ConDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// Constructor name.
    pub name: Ident,
    /// Constructor fields.
    pub fields: ConFields,
    /// Existentially quantified type variables (from `forall a.` syntax).
    pub existential_vars: Vec<TyVar>,
    /// Existential context constraints (from `C a =>` syntax).
    pub existential_context: Vec<Constraint>,
    /// The span.
    pub span: Span,
}

/// Constructor fields.
#[derive(Clone, Debug)]
pub enum ConFields {
    /// Positional: `Foo Int String`
    Positional(Vec<Type>),
    /// Record: `Foo { bar :: Int, baz :: String }`
    Record(Vec<FieldDecl>),
}

/// A record field declaration.
#[derive(Clone, Debug)]
pub struct FieldDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// Field name.
    pub name: Ident,
    /// Field type.
    pub ty: Type,
    /// The span.
    pub span: Span,
}

/// A type alias declaration.
#[derive(Clone, Debug)]
pub struct TypeAlias {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The alias name.
    pub name: Ident,
    /// Type parameters.
    pub params: Vec<TyVar>,
    /// The aliased type.
    pub ty: Type,
    /// The span.
    pub span: Span,
}

/// A newtype declaration.
#[derive(Clone, Debug)]
pub struct NewtypeDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// The type name.
    pub name: Ident,
    /// Type parameters.
    pub params: Vec<TyVar>,
    /// The constructor.
    pub constr: ConDecl,
    /// Deriving clauses.
    pub deriving: Vec<DerivingClause>,
    /// The span.
    pub span: Span,
}

/// A type class declaration.
#[derive(Clone, Debug)]
pub struct ClassDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// Superclass constraints.
    pub context: Vec<Constraint>,
    /// Class name.
    pub name: Ident,
    /// Type parameters (multi-param type classes).
    pub params: Vec<TyVar>,
    /// Functional dependencies.
    pub fundeps: Vec<FunDep>,
    /// Associated type declarations.
    pub assoc_types: Vec<AssocType>,
    /// Method signatures and default implementations.
    pub methods: Vec<Decl>,
    /// The span.
    pub span: Span,
}

/// An associated type declaration within a type class.
///
/// Example: `type Elem c` in `class Collection c where type Elem c`
#[derive(Clone, Debug)]
pub struct AssocType {
    /// The name of the associated type.
    pub name: Ident,
    /// Type parameters (in addition to class params).
    pub params: Vec<TyVar>,
    /// Optional kind signature.
    pub kind: Option<Kind>,
    /// Optional default type.
    pub default: Option<Type>,
    /// The span.
    pub span: Span,
}

/// A kind (for kind signatures).
#[derive(Clone, Debug)]
pub enum Kind {
    /// The kind of types: `*` or `Type`.
    Star,
    /// Function kind: `k1 -> k2`.
    Arrow(Box<Kind>, Box<Kind>),
    /// Named kind variable.
    Var(Ident),
}

/// A functional dependency in a type class.
#[derive(Clone, Debug)]
pub struct FunDep {
    /// Variables that determine others.
    pub from: Vec<Ident>,
    /// Variables that are determined.
    pub to: Vec<Ident>,
    /// The span.
    pub span: Span,
}

/// An instance declaration.
#[derive(Clone, Debug)]
pub struct InstanceDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// Instance constraints.
    pub context: Vec<Constraint>,
    /// Class name.
    pub class: Ident,
    /// Instance type.
    pub ty: Type,
    /// Associated type definitions.
    pub assoc_type_defs: Vec<AssocTypeDef>,
    /// Method implementations.
    pub methods: Vec<Decl>,
    /// The span.
    pub span: Span,
}

/// An associated type definition within an instance.
///
/// Example: `type Elem [a] = a` in `instance Collection [a] where type Elem [a] = a`
#[derive(Clone, Debug)]
pub struct AssocTypeDef {
    /// The name of the associated type.
    pub name: Ident,
    /// Type arguments (patterns for the associated type).
    pub args: Vec<Type>,
    /// The definition (right-hand side).
    pub rhs: Type,
    /// The span.
    pub span: Span,
}

/// Safety annotation for foreign declarations.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ForeignSafety {
    /// Safe FFI call (default). May trigger GC, slower.
    Safe,
    /// Unsafe FFI call. Must be fast, no callbacks.
    Unsafe,
    /// Interruptible FFI call. Can be interrupted by async exceptions.
    Interruptible,
}

/// A foreign declaration.
#[derive(Clone, Debug)]
pub struct ForeignDecl {
    /// Documentation comment.
    pub doc: Option<DocComment>,
    /// Import or export.
    pub kind: ForeignKind,
    /// Calling convention.
    pub convention: Symbol,
    /// Safety annotation.
    pub safety: ForeignSafety,
    /// External name.
    pub external_name: Option<String>,
    /// Haskell name.
    pub name: Ident,
    /// Type signature.
    pub ty: Type,
    /// The span.
    pub span: Span,
}

/// Foreign declaration kind.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ForeignKind {
    /// Foreign import.
    Import,
    /// Foreign export.
    Export,
}

/// A fixity declaration.
#[derive(Clone, Debug)]
pub struct FixityDecl {
    /// The fixity.
    pub fixity: Fixity,
    /// Precedence level (0-9).
    pub prec: u8,
    /// The operators.
    pub ops: Vec<Ident>,
    /// The span.
    pub span: Span,
}

/// Operator fixity.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Fixity {
    /// Left associative.
    Left,
    /// Right associative.
    Right,
    /// Non-associative.
    None,
}

/// An expression.
#[derive(Clone, Debug)]
pub enum Expr {
    /// Variable: `x`
    Var(Ident, Span),
    /// Qualified variable: `M.foo`, `Data.List.sort`
    QualVar(ModuleName, Ident, Span),
    /// Constructor: `Just`
    Con(Ident, Span),
    /// Qualified constructor: `M.Just`, `Data.Maybe.Nothing`
    QualCon(ModuleName, Ident, Span),
    /// Literal: `42`, `"hello"`
    Lit(Lit, Span),
    /// Application: `f x`
    App(Box<Expr>, Box<Expr>, Span),
    /// Lambda: `\x -> x`
    Lam(Vec<Pat>, Box<Expr>, Span),
    /// Let: `let x = 1 in x`
    Let(Vec<Decl>, Box<Expr>, Span),
    /// If: `if c then t else e`
    If(Box<Expr>, Box<Expr>, Box<Expr>, Span),
    /// Case: `case x of { ... }`
    Case(Box<Expr>, Vec<Alt>, Span),
    /// Do block: `do { ... }`
    Do(Vec<Stmt>, Span),
    /// Tuple: `(a, b, c)`
    Tuple(Vec<Expr>, Span),
    /// List: `[1, 2, 3]`
    List(Vec<Expr>, Span),
    /// Arithmetic sequence: `[1..10]`, `[1,3..10]`
    ArithSeq(ArithSeq, Span),
    /// List comprehension: `[x | x <- xs, x > 0]`
    ListComp(Box<Expr>, Vec<Stmt>, Span),
    /// Record construction: `Foo { bar = 1 }` or `Foo { bar = 1, .. }` (RecordWildCards)
    RecordCon(Ident, Vec<FieldBind>, bool, Span),
    /// Qualified record construction: `M.Foo { bar = 1 }`
    QualRecordCon(ModuleName, Ident, Vec<FieldBind>, bool, Span),
    /// Record update: `foo { bar = 1 }`
    RecordUpd(Box<Expr>, Vec<FieldBind>, Span),
    /// Infix operator: `a + b`
    Infix(Box<Expr>, Ident, Box<Expr>, Span),
    /// Negation: `-x`
    Neg(Box<Expr>, Span),
    /// Parenthesized expression
    Paren(Box<Expr>, Span),
    /// Type annotation: `x :: Int`
    Ann(Box<Expr>, Type, Span),
    /// Explicit type application: `f @Int`
    TypeApp(Box<Expr>, Type, Span),
    /// Lazy block (H26): `lazy { ... }`
    Lazy(Box<Expr>, Span),
    /// Wildcard/hole: `_` (used in patterns parsed as expressions)
    Wildcard(Span),
}

impl Expr {
    /// Get the span of this expression.
    #[must_use]
    pub fn span(&self) -> Span {
        match self {
            Self::Var(_, s)
            | Self::QualVar(_, _, s)
            | Self::Con(_, s)
            | Self::QualCon(_, _, s)
            | Self::Lit(_, s)
            | Self::App(_, _, s)
            | Self::Lam(_, _, s)
            | Self::Let(_, _, s)
            | Self::If(_, _, _, s)
            | Self::Case(_, _, s)
            | Self::Do(_, s)
            | Self::Tuple(_, s)
            | Self::List(_, s)
            | Self::ArithSeq(_, s)
            | Self::ListComp(_, _, s)
            | Self::RecordCon(_, _, _, s)
            | Self::QualRecordCon(_, _, _, _, s)
            | Self::RecordUpd(_, _, s)
            | Self::Infix(_, _, _, s)
            | Self::Neg(_, s)
            | Self::Paren(_, s)
            | Self::Ann(_, _, s)
            | Self::TypeApp(_, _, s)
            | Self::Lazy(_, s)
            | Self::Wildcard(s) => *s,
        }
    }
}

/// A literal value.
#[derive(Clone, Debug, PartialEq)]
pub enum Lit {
    /// Integer literal.
    Int(i64),
    /// Floating-point literal.
    Float(f64),
    /// Character literal.
    Char(char),
    /// String literal.
    String(String),
}

/// An arithmetic sequence.
#[derive(Clone, Debug)]
pub enum ArithSeq {
    /// `[from..]`
    From(Box<Expr>),
    /// `[from, then..]`
    FromThen(Box<Expr>, Box<Expr>),
    /// `[from..to]`
    FromTo(Box<Expr>, Box<Expr>),
    /// `[from, then..to]`
    FromThenTo(Box<Expr>, Box<Expr>, Box<Expr>),
}

/// A case alternative.
#[derive(Clone, Debug)]
pub struct Alt {
    /// The pattern.
    pub pat: Pat,
    /// The right-hand side.
    pub rhs: Rhs,
    /// Local bindings.
    pub wheres: Vec<Decl>,
    /// The span.
    pub span: Span,
}

/// A statement in a do block or list comprehension.
#[derive(Clone, Debug)]
pub enum Stmt {
    /// Generator: `x <- xs`
    Generator(Pat, Expr, Span),
    /// Qualifier/guard: `x > 0`
    Qualifier(Expr, Span),
    /// Let binding: `let x = 1`
    LetStmt(Vec<Decl>, Span),
}

/// A field binding in a record.
#[derive(Clone, Debug)]
pub struct FieldBind {
    /// Optional module qualifier for disambiguated record fields (e.g., `XMonad.borderWidth`).
    pub qualifier: Option<ModuleName>,
    /// Field name.
    pub name: Ident,
    /// Field value (None for punning: `Foo { bar }` means `Foo { bar = bar }`)
    pub value: Option<Expr>,
    /// The span.
    pub span: Span,
}

/// A pattern.
#[derive(Clone, Debug)]
pub enum Pat {
    /// Wildcard: `_`
    Wildcard(Span),
    /// Variable: `x`
    Var(Ident, Span),
    /// Literal: `42`
    Lit(Lit, Span),
    /// Constructor: `Just x`
    Con(Ident, Vec<Pat>, Span),
    /// Qualified constructor: `M.Just x`, `Data.Maybe.Nothing`
    QualCon(ModuleName, Ident, Vec<Pat>, Span),
    /// Infix constructor: `x : xs`
    Infix(Box<Pat>, Ident, Box<Pat>, Span),
    /// Tuple: `(a, b)`
    Tuple(Vec<Pat>, Span),
    /// List: `[a, b, c]`
    List(Vec<Pat>, Span),
    /// Record: `Foo { bar = x }` or `Foo { bar = x, .. }` (RecordWildCards)
    Record(Ident, Vec<FieldPat>, bool, Span),
    /// Qualified record: `M.Foo { bar = x }` or `M.Foo { .. }` (RecordWildCards)
    QualRecord(ModuleName, Ident, Vec<FieldPat>, bool, Span),
    /// As-pattern: `xs@(x:_)`
    As(Ident, Box<Pat>, Span),
    /// Lazy pattern: `~pat`
    Lazy(Box<Pat>, Span),
    /// Bang pattern: `!pat`
    Bang(Box<Pat>, Span),
    /// Parenthesized pattern
    Paren(Box<Pat>, Span),
    /// Type annotation: `x :: Int`
    Ann(Box<Pat>, Type, Span),
    /// View pattern: `(expr -> pat)` (ViewPatterns extension)
    View(Box<Expr>, Box<Pat>, Span),
}

impl Pat {
    /// Get the span of this pattern.
    #[must_use]
    pub fn span(&self) -> Span {
        match self {
            Self::Wildcard(s)
            | Self::Var(_, s)
            | Self::Lit(_, s)
            | Self::Con(_, _, s)
            | Self::QualCon(_, _, _, s)
            | Self::Infix(_, _, _, s)
            | Self::Tuple(_, s)
            | Self::List(_, s)
            | Self::Record(_, _, _, s)
            | Self::QualRecord(_, _, _, _, s)
            | Self::As(_, _, s)
            | Self::Lazy(_, s)
            | Self::Bang(_, s)
            | Self::Paren(_, s)
            | Self::Ann(_, _, s)
            | Self::View(_, _, s) => *s,
        }
    }
}

/// A field pattern in a record.
#[derive(Clone, Debug)]
pub struct FieldPat {
    /// Optional module qualifier for disambiguated record fields (e.g., `XMonad.modMask`).
    pub qualifier: Option<ModuleName>,
    /// Field name.
    pub name: Ident,
    /// Pattern (None for punning).
    pub pat: Option<Pat>,
    /// The span.
    pub span: Span,
}

/// A type.
#[derive(Clone, Debug)]
pub enum Type {
    /// Type variable: `a`
    Var(TyVar, Span),
    /// Type constructor: `Int`, `Maybe`
    Con(Ident, Span),
    /// Qualified type constructor: `M.Map`, `Data.List.Sort`
    QualCon(ModuleName, Ident, Span),
    /// Application: `Maybe Int`
    App(Box<Type>, Box<Type>, Span),
    /// Function type: `a -> b`
    Fun(Box<Type>, Box<Type>, Span),
    /// Tuple type: `(a, b)`
    Tuple(Vec<Type>, Span),
    /// List type: `[a]`
    List(Box<Type>, Span),
    /// Parenthesized type
    Paren(Box<Type>, Span),
    /// Forall type: `forall a. a -> a`
    Forall(Vec<TyVar>, Box<Type>, Span),
    /// Constrained type: `Eq a => a -> a -> Bool`
    Constrained(Vec<Constraint>, Box<Type>, Span),

    // === M9 Dependent Types Preview ===
    /// Promoted list: `'[1024, 768]` for tensor shapes
    PromotedList(Vec<Type>, Span),
    /// Type-level natural literal: `1024` in type position
    NatLit(u64, Span),

    /// Strict type annotation: `!Int` in constructor fields
    Bang(Box<Type>, Span),

    /// Lazy type annotation: `~Int` in constructor fields
    Lazy(Box<Type>, Span),

    /// Infix type operator: `a :+: b` (requires TypeOperators)
    InfixOp(Box<Type>, Ident, Box<Type>, Span),
}

impl Type {
    /// Get the span of this type.
    #[must_use]
    pub fn span(&self) -> Span {
        match self {
            Self::Var(_, s)
            | Self::Con(_, s)
            | Self::QualCon(_, _, s)
            | Self::App(_, _, s)
            | Self::Fun(_, _, s)
            | Self::Tuple(_, s)
            | Self::List(_, s)
            | Self::Paren(_, s)
            | Self::Forall(_, _, s)
            | Self::Constrained(_, _, s)
            | Self::PromotedList(_, s)
            | Self::NatLit(_, s)
            | Self::Bang(_, s)
            | Self::Lazy(_, s)
            | Self::InfixOp(_, _, _, s) => *s,
        }
    }
}

/// A type variable.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyVar {
    /// The name.
    pub name: Ident,
    /// The span.
    pub span: Span,
}

/// A type class constraint.
#[derive(Clone, Debug)]
pub struct Constraint {
    /// The class name.
    pub class: Ident,
    /// The type arguments.
    pub args: Vec<Type>,
    /// The span.
    pub span: Span,
}
