"""
Letter attributes and data structures.

This module defines attributes specific to the Letter module,
including Alphabets, Enochian letters, and Double Letter Trumps.
"""

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Tuple

from utils.attributes import (
    ElementType,
    Meaning,
    Planet,
)


@dataclass
class Letter:
    """Represents a letter with its attributes."""

    character: str
    position: int
    name: str


@dataclass
class EnglishAlphabet:
    """English alphabet with Tarot/Kabbalistic correspondence."""

    letter: str
    position: int
    sound: str

    def __post_init__(self) -> None:
        if not (1 <= self.position <= 26):
            raise ValueError(f"Position must be between 1 and 26, got {self.position}")
        if len(self.letter) != 1 or not self.letter.isalpha():
            raise ValueError(f"Letter must be a single alphabetic character, got {self.letter}")


@dataclass
class GreekAlphabet:
    """Greek alphabet with Tarot/Kabbalistic correspondence."""

    letter: str
    position: int
    transliteration: str

    def __post_init__(self) -> None:
        if not (1 <= self.position <= 24):
            raise ValueError(f"Position must be between 1 and 24, got {self.position}")


@dataclass
class HebrewAlphabet:
    """Hebrew alphabet with Tarot/Kabbalistic correspondence."""

    letter: str
    position: int
    transliteration: str
    meaning: str

    def __post_init__(self) -> None:
        if not (1 <= self.position <= 22):
            raise ValueError(f"Position must be between 1 and 22, got {self.position}")


@dataclass
class DoublLetterTrump:
    """Represents a Double Letter Trump (Yodh through Tau, 3-21 of Major Arcana)."""

    number: int  # 3-21 (19 double letter trumps)
    name: str  # Full name (e.g., "The Empress")
    hebrew_letter_1: str  # First Hebrew letter (e.g., "Gimel")
    hebrew_letter_2: Optional[str] = None  # Second Hebrew letter if applicable
    planet: Optional["Planet"] = None  # Associated planet
    tarot_trump: Optional[str] = None  # e.g., "III - The Empress"
    astrological_sign: Optional[str] = None  # Zodiac sign if any
    element: Optional["ElementType"] = None  # Associated element
    number_value: Optional[int] = None  # Numerological value
    keywords: List[str] = field(default_factory=list)
    meaning: Optional["Meaning"] = None  # Upright and reversed meanings
    description: str = ""

    def __post_init__(self) -> None:
        if not 3 <= self.number <= 21:
            raise ValueError(f"Double Letter Trump number must be 3-21, got {self.number}")


@dataclass(frozen=True)
class EnochianLetter:
    """Represents an Enochian letter with its properties."""

    name: str  # Enochian letter name
    letter: str  # The letter itself
    hebrew_equivalent: Optional[str] = None
    tarot_correspondence: Optional[str] = None
    planet: Optional[str] = None
    element: Optional[str] = None
    keywords: List[str] = field(default_factory=list)
    description: str = ""


@dataclass(frozen=True)
class EnochianSpirit:
    """Represents an Enochian spirit or intelligence."""

    name: str  # Spirit name
    rank: str  # e.g., "King", "Prince", "Duke", "Intelligence"
    element: Optional[str] = None
    direction: Optional[str] = None  # e.g., "East", "South", etc.
    sigil: Optional[str] = None  # ASCII representation or description
    keywords: List[str] = field(default_factory=list)
    description: str = ""


@dataclass(frozen=True)
class EnochianArchetype:
    """
    Archetypal form of an Enochian Tablet.

    Provides a 4x4 grid with positions that can be filled with different
    visual representations (colors, images, symbols, etc.).
    """

    name: str  # e.g., "Tablet of Air Archetype"
    tablet_name: str  # Reference to parent tablet
    grid: Dict[Tuple[int, int], "EnochianGridPosition"] = field(default_factory=dict)  # 4x4 grid
    row_correspondences: List[Dict[str, Any]] = field(default_factory=list)  # Row meanings (4 rows)
    col_correspondences: List[Dict[str, Any]] = field(
        default_factory=list
    )  # Column meanings (4 cols)
    keywords: List[str] = field(default_factory=list)
    description: str = ""

    def get_position(self, row: int, col: int) -> Optional["EnochianGridPosition"]:
        """Get the grid position at (row, col)."""
        if not 0 <= row < 4 or not 0 <= col < 4:
            return None
        return self.grid.get((row, col))

    def get_row_correspondence(self, row: int) -> Optional[Dict[str, Any]]:
        """Get the meaning/correspondence for a row."""
        if 0 <= row < len(self.row_correspondences):
            return self.row_correspondences[row]
        return None

    def get_col_correspondence(self, col: int) -> Optional[Dict[str, Any]]:
        """Get the meaning/correspondence for a column."""
        if 0 <= col < len(self.col_correspondences):
            return self.col_correspondences[col]
        return None


@dataclass(frozen=True)
class EnochianGridPosition:
    """
    Represents a single position in an Enochian Tablet grid.

    A 4x4 grid cell with:
    - Center letter
    - Directional letters (N, S, E, W)
    - Archetypal correspondences (Tarot, element, etc.)
    """

    row: int  # Grid row (0-3)
    col: int  # Grid column (0-3)
    center_letter: str  # The main letter at this position
    north_letter: Optional[str] = None  # Letter above
    south_letter: Optional[str] = None  # Letter below
    east_letter: Optional[str] = None  # Letter to the right
    west_letter: Optional[str] = None  # Letter to the left
    tarot_card: Optional[str] = None  # Associated Tarot card (e.g., "Ace of Swords")
    tarot_suit: Optional[str] = None  # Suit correspondence (Swords, Wands, Cups, Pentacles)
    tarot_number: Optional[int] = None  # Card number (0-13, 0=Ace)
    element: Optional[str] = None  # Element correspondence
    zodiac_sign: Optional[str] = None  # Zodiac correspondence
    planetary_hour: Optional[str] = None  # Associated hour
    keywords: List[str] = field(default_factory=list)
    meanings: List[str] = field(default_factory=list)

    def get_all_letters(self) -> Dict[str, str]:
        """Get all letters in this position: center and directional."""
        letters = {"center": self.center_letter}
        if self.north_letter:
            letters["north"] = self.north_letter
        if self.south_letter:
            letters["south"] = self.south_letter
        if self.east_letter:
            letters["east"] = self.east_letter
        if self.west_letter:
            letters["west"] = self.west_letter
        return letters


@dataclass(frozen=True)
class EnochianTablet:
    """
    Represents an Enochian Tablet.

    The Enochian system contains:
    - 4 elemental tablets (Earth, Water, Air, Fire)
    - 1 tablet of union (Aethyr)
    - Each tablet is 12x12 (144 squares) containing Enochian letters
    - Archetypal form with 4x4 grid for user customization
    """

    name: str  # e.g., "Tablet of Earth", "Tablet of Air", etc.
    number: int  # Tablet identifier (1-5)
    element: Optional[str] = None  # Earth, Water, Air, Fire, or Aethyr/Union
    rulers: List[str] = field(default_factory=list)  # Names of spirits ruling this tablet
    archangels: List[str] = field(default_factory=list)  # Associated archangels
    letters: Dict[Tuple[int, int], str] = field(
        default_factory=dict
    )  # Grid of letters (row, col) -> letter
    planetary_hours: List[str] = field(default_factory=list)  # Associated hours
    keywords: List[str] = field(default_factory=list)
    description: str = ""
    archetype: Optional[EnochianArchetype] = None  # Archetypal form for visualization

    # Valid tablets
    VALID_TABLETS = {
        "Tablet of Union",  # Aethyr
        "Tablet of Earth",
        "Tablet of Water",
        "Tablet of Air",
        "Tablet of Fire",
    }

    def __post_init__(self) -> None:
        if self.name not in self.VALID_TABLETS:
            raise ValueError(
                f"Invalid tablet '{self.name}'. " f"Valid tablets: {', '.join(self.VALID_TABLETS)}"
            )
        # Tablet of Union uses 0, elemental tablets use 1-5
        valid_range = (0, 0) if "Union" in self.name else (1, 5)
        if not valid_range[0] <= self.number <= valid_range[1]:
            raise ValueError(
                f"Tablet number must be {valid_range[0]}-{valid_range[1]}, got {self.number}"
            )

    def is_elemental(self) -> bool:
        """Check if this is an elemental tablet (not union)."""
        return self.element in {"Earth", "Water", "Air", "Fire"}

    def is_union(self) -> bool:
        """Check if this is the Tablet of Union (Aethyr)."""
        return self.element == "Aethyr" or "Union" in self.name

    def get_letter(self, row: int, col: int) -> Optional[str]:
        """Get letter at specific grid position."""
        return self.letters.get((row, col))

    def get_row(self, row: int) -> List[Optional[str]]:
        """Get all letters in a row."""
        return [self.letters.get((row, col)) for col in range(12)]

    def get_column(self, col: int) -> List[Optional[str]]:
        """Get all letters in a column."""
        return [self.letters.get((row, col)) for row in range(12)]
