"""
Tarot Letters namespace - Hebrew letters with full correspondences.

Provides fluent access to Hebrew letters (Paths on Tree of Life) organized by type:
- Mother Letters (3): Aleph, Mem, Shin
- Double Letters (7): Beth through Tzadi
- Simple Letters (12): Yodh through Tau

Data is sourced from CardDataLoader.paths() for a single source of truth.
Each letter has attributes like:
  - Hebrew Letter
  - Zodiac (for simple letters)
  - Trump (Major Arcana card)
  - Four Color System (King, Queen, Prince, Princess)
  - Cube of Space correspondence
  - Intelligence/Archangel
  - Musical Note
"""

from dataclasses import dataclass
from typing import TYPE_CHECKING, Dict, List, Optional, Union

from utils.filter import format_results, get_filterable_fields, universal_filter

if TYPE_CHECKING:
    from tarot.attributes import Path
    from utils.query import CollectionAccessor


@dataclass
class TarotLetter:
    """
    Represents a Hebrew letter with full Tarot correspondences.

    Wraps Path objects from CardDataLoader to provide a letter-focused interface
    while maintaining a single source of truth.
    """

    path: "Path"  # Reference to the actual Path object from CardDataLoader
    letter_type: str  # "Mother", "Double", or "Simple" (derived from path)

    def __post_init__(self) -> None:
        """Validate that path is set."""
        if not self.path:
            raise ValueError("TarotLetter requires a valid Path object")

    @property
    def hebrew_letter(self) -> str:
        """Get Hebrew letter character."""
        return self.path.hebrew_letter or ""

    @property
    def transliteration(self) -> str:
        """Get transliterated name."""
        return self.path.transliteration or ""

    @property
    def position(self) -> int:
        """Get position (1-22 for paths)."""
        return self.path.number

    @property
    def trump(self) -> Optional[str]:
        """Get Tarot trump designation."""
        return self.path.tarot_trump

    @property
    def element(self) -> Optional[str]:
        """Get element name if applicable."""
        return self.path.element.name if self.path.element else None

    @property
    def planet(self) -> Optional[str]:
        """Get planet name if applicable."""
        return self.path.planet.name if self.path.planet else None

    @property
    def zodiac(self) -> Optional[str]:
        """Get zodiac sign if applicable."""
        return self.path.zodiac_sign

    @property
    def intelligence(self) -> Optional[str]:
        """Get archangel/intelligence name from associated gods."""
        # Extract first god's name from the path's associated gods
        all_gods = self.path.get_gods()
        if all_gods:
            return all_gods[0].name
        return None

    @property
    def meaning(self) -> Optional[str]:
        """Get path meaning/description."""
        return self.path.description

    @property
    def keywords(self) -> List[str]:
        """Get keywords associated with path."""
        return self.path.keywords or []

    def display(self) -> str:
        """Format letter for display."""
        lines = [
            f"Hebrew: {self.hebrew_letter}",
            f"Name: {self.transliteration}",
            f"Type: {self.letter_type}",
            f"Position: {self.position}",
        ]

        if self.trump:
            lines.append(f"Trump: {self.trump}")
        if self.zodiac:
            lines.append(f"Zodiac: {self.zodiac}")
        if self.planet:
            lines.append(f"Planet: {self.planet}")
        if self.element:
            lines.append(f"Element: {self.element}")
        if self.intelligence:
            lines.append(f"Intelligence: {self.intelligence}")
        if self.meaning:
            lines.append(f"Meaning: {self.meaning}")
        if self.keywords:
            lines.append(f"Keywords: {', '.join(self.keywords)}")

        return "\n".join(lines)


class LetterAccessor:
    """Fluent accessor for Tarot letters."""

    def __init__(self, letters_dict: Dict[str, TarotLetter]) -> None:
        self._letters = letters_dict

    def __call__(self, transliteration: str) -> Optional[TarotLetter]:
        """Get a letter by transliteration (e.g., 'aleph', 'beth', 'gimel')."""
        return self._letters.get(transliteration.lower())

    def __getitem__(self, key: Union[str, int]) -> Optional[TarotLetter]:
        """Get letter by name or position."""
        if isinstance(key, int):
            # Get by position (1-22)
            for letter in self._letters.values():
                if letter.position == key:
                    return letter
            return None
        return self(key)

    def all(self) -> List[TarotLetter]:
        """Get all letters."""
        return sorted(self._letters.values(), key=lambda x: x.position)

    def by_type(self, letter_type: str) -> List[TarotLetter]:
        """Filter by type: 'Mother', 'Double', or 'Simple'."""
        return [letter for letter in self._letters.values() if letter.letter_type == letter_type]

    def by_zodiac(self, zodiac: str) -> Optional[TarotLetter]:
        """Get letter by zodiac sign."""
        for letter in self._letters.values():
            if letter.zodiac and zodiac.lower() in letter.zodiac.lower():
                return letter
        return None

    def by_planet(self, planet: str) -> List[TarotLetter]:
        """Get letters by planet."""
        return [
            letter
            for letter in self._letters.values()
            if letter.planet and planet.lower() in letter.planet.lower()
        ]

    def by_trump(self, trump: str) -> Optional[TarotLetter]:
        """Get letter by tarot trump."""
        return next((letter for letter in self._letters.values() if letter.trump == trump), None)

    def get_filterable_fields(self) -> List[str]:
        """
        Dynamically get all filterable fields from TarotLetter.

        Returns the same fields as the universal filter utility.
        Useful for introspection and validation.
        """
        return get_filterable_fields(TarotLetter)

    def filter(self, **kwargs) -> List[TarotLetter]:
        """
        Filter letters by any TarotLetter attribute.

        Uses the universal filter from utils.filter for consistency
        across the entire project.

        The filter automatically handles all fields from the TarotLetter dataclass:
        - letter_type, element, trump, zodiac, planet
        - king, queen, prince, princess
        - cube, intelligence, note, meaning, hebrew_letter, transliteration, position
        - keywords (list matching)

        Args:
            **kwargs: Any TarotLetter attribute with its value

        Usage:
            Tarot.letters.filter(letter_type="Simple")
            Tarot.letters.filter(element="Fire")
            Tarot.letters.filter(letter_type="Double", planet="Mars")
            Tarot.letters.filter(element="Air", letter_type="Mother")
            Tarot.letters.filter(intelligence="Metatron")
            Tarot.letters.filter(position=1)

        Returns:
            List of TarotLetter objects matching all filters
        """
        return universal_filter(self.all(), **kwargs)

    def display_filter(self, **kwargs) -> str:
        """
        Filter letters and display results nicely formatted.

        Combines filtering and formatting in one call.

        Args:
            **kwargs: Any TarotLetter attribute with its value

        Returns:
            Formatted string with filtered letters

        Example:
            print(Tarot.letters.display_filter(element="Fire"))
        """
        results = self.filter(**kwargs)
        return format_results(results)

    def display_all(self) -> str:
        """Display all letters formatted."""
        lines = []
        for letter in self.all():
            lines.append(letter.display())
            lines.append("-" * 50)
        return "\n".join(lines)

    def display_by_type(self, letter_type: str) -> str:
        """Display all letters of a specific type."""
        letters = self.by_type(letter_type)
        if not letters:
            return f"No letters found with type: {letter_type}"

        lines = [f"\n{letter_type.upper()} LETTERS ({len(letters)} total)"]
        lines.append("=" * 50)
        for letter in letters:
            lines.append(letter.display())
            lines.append("-" * 50)
        return "\n".join(lines)

    @property
    def iChing(self):
        """Access I Ching trigrams and hexagrams."""
        return IChing()


class IChing:
    """Namespace for I Ching trigrams and hexagrams access.

    Provides fluent query interface for accessing I Ching trigrams and hexagrams
    with Tarot correspondences.

    Usage:
        trigrams = Tarot.letters.iChing.trigram
        qian = trigrams.name('Qian')
        all_trigrams = trigrams.all()

        hexagrams = Tarot.letters.iChing.hexagram
        hex1 = hexagrams.all()[1]
        all_hex = hexagrams.list()
    """

    trigram: "CollectionAccessor"
    hexagram: "CollectionAccessor"

    def __init__(self) -> None:
        """Initialize iChing accessor with trigram and hexagram collections."""
        from tarot.letter import iChing as iching_module

        self.trigram = iching_module.trigram.trigram
        self.hexagram = iching_module.hexagram.hexagram

    def __repr__(self) -> str:
        """Clean representation of iChing namespace."""
        return "IChing(trigram, hexagram)"

    def __str__(self) -> str:
        """String representation of iChing namespace."""
        return "I Ching (trigrams and hexagrams)"


class LettersRegistry:
    """Registry and accessor for all Hebrew letters with Tarot correspondences."""

    _instance: Optional["LettersRegistry"] = None
    _letters: Dict[str, TarotLetter] = {}
    _initialized: bool = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self) -> None:
        if self._initialized:
            return

        self._initialize_letters()
        self._initialized = True

    def _initialize_letters(self) -> None:
        """Initialize all 22 Hebrew letters by wrapping Path objects from CardDataLoader."""
        from tarot.card.data import CardDataLoader

        loader = CardDataLoader()
        paths = loader.path()  # Get all 22 paths

        self._letters = {}

        # Map each path (11-32) to a TarotLetter with appropriate type
        for path_number, path in paths.items():
            # Determine letter type based on path number
            # Mother letters: 11 (Aleph), 23 (Mem), 31 (Shin)
            # Double letters: 12, 13, 14, 15, 18, 21, 22
            # Simple (Zodiacal/Planetary): 16, 17, 19, 20, 24, 25, 26, 27, 28, 29, 30, 32

            if path_number in {11, 23, 31}:
                letter_type = "Mother"
            elif path_number in {12, 13, 14, 15, 18, 21, 22}:
                letter_type = "Double"
            else:
                letter_type = "Simple"

            # Create TarotLetter wrapping the path
            letter_key = path.transliteration.lower()
            self._letters[letter_key] = TarotLetter(path=path, letter_type=letter_type)

    def accessor(self) -> LetterAccessor:
        """Get the letter accessor."""
        return LetterAccessor(self._letters)


def letters() -> LetterAccessor:
    """Get the letters accessor for fluent queries."""
    registry = LettersRegistry()
    return registry.accessor()
