"""
Tarot deck and card accessor module.

Provides fluent access to Tarot cards through Tarot.deck namespace.

Usage:
    from tarot.card import Deck, Card

    card = Deck.card(3)                             # Get card 3
    cards = Deck.card.filter(arcana="Major")   # Get all Major Arcana
    cards = Deck.card.filter(arcana="Minor")   # Get all Minor Arcana
    cards = Deck.card.filter(suit="Cups")           # Get all Cups
    cards = Deck.card.filter(arcana="Minor", suit="Wands", pip=5)  # 5 of Wands
"""

from typing import TYPE_CHECKING, List, Optional

from utils.filter import format_results, universal_filter
from utils.object_formatting import format_value, get_object_attributes, is_nested_object

if TYPE_CHECKING:
    from tarot.deck import Card, Deck


class CardList(list):
    """Custom list class for cards that formats nicely when printed."""

    def __str__(self) -> str:
        """Format card list for display."""
        if not self:
            return "(no cards)"
        return _format_cards(self)

    def __repr__(self) -> str:
        """Return string representation."""
        return self.__str__()


def _format_cards(cards: List["Card"]) -> str:
    """
    Format a list of cards for user-friendly display.

    Args:
        cards: List of Card objects to format

    Returns:
        Formatted string with each card separated by blank lines
    """
    lines = []
    for card in cards:
        card_num = getattr(card, "number", "?")
        card_name = getattr(card, "name", "Unknown")
        lines.append(f"--- {card_num}: {card_name} ---")

        # Format all attributes with proper nesting
        for attr_name, attr_value in get_object_attributes(card):
            if is_nested_object(attr_value):
                lines.append(f"  {attr_name}:")
                lines.append(f"    --- {attr_name.replace('_', ' ').title()} ---")
                nested = format_value(attr_value, indent=4)
                lines.append(nested)
            else:
                lines.append(f"  {attr_name}: {attr_value}")

        lines.append("")  # Blank line between items

    return "\n".join(lines)


class CardAccessor:
    """
    Fluent accessor for Tarot cards in the deck.

    Usage:
        Tarot.deck.card(3)                                          # Get card 3
        Tarot.deck.card.filter(arcana="Major")                # Get all Major Arcana
        Tarot.deck.card.filter(arcana="Minor")                # Get all Minor Arcana
        Tarot.deck.card.filter(suit="Cups")                        # Get all Cups
        Tarot.deck.card.filter(arcana="Minor", suit="Wands") # Get all Wand cards
        Tarot.deck.card.display_filter(arcana="Major")        # Display Major Arcana
    """

    _deck: Optional["Deck"] = None
    _initialized: bool = False

    def _ensure_initialized(self) -> None:
        """Lazy-load the Deck on first access."""
        if not self._initialized:
            from tarot.deck import Deck as DeckClass

            CardAccessor._deck = DeckClass()
            CardAccessor._initialized = True

    def __call__(self, number: int) -> Optional["Card"]:
        """Get a card by number."""
        self._ensure_initialized()
        if self._deck is None:
            return None
        for card in self._deck.cards:
            if card.number == number:
                return card
        return None

    def filter(self, **kwargs) -> CardList:
        """
        Filter cards by any Card attribute.

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

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

        Usage:
            Tarot.deck.card.filter(arcana="Major")
            Tarot.deck.card.filter(arcana="Minor", suit="Cups")
            Tarot.deck.card.filter(number=5)
            Tarot.deck.card.filter(element="Fire")
            Tarot.deck.card.filter(pip=3)

        Returns:
            CardList of Card objects matching all filters
        """
        self._ensure_initialized()
        if self._deck is None:
            return CardList()
        return CardList(universal_filter(self._deck.cards, **kwargs))

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

        Combines filtering and formatting in one call.

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

        Returns:
            Formatted string with filtered cards

        Example:
            print(Tarot.deck.card.display_filter(arcana="Major"))
        """
        results = self.filter(**kwargs)
        return format_results(results)

    def display(self) -> str:
        """
        Format all cards in the deck for user-friendly display.

        Returns a formatted string with each card separated by blank lines.
        Nested objects are indented and separated with their own sections.
        """
        self._ensure_initialized()
        if self._deck is None:
            return "(deck not initialized)"
        return _format_cards(self._deck.cards)

    def __str__(self) -> str:
        """Return the complete Tarot deck structure built from actual cards."""
        self._ensure_initialized()
        if self._deck is None:
            return "CardAccessor (deck not initialized)"

        lines = [
            "Tarot Deck Structure",
            "=" * 60,
            "",
            "The 78-card Tarot deck organized by structure and correspondence:",
            "",
        ]

        # Build structure from actual cards
        major_arcana = [c for c in self._deck.cards if c.arcana == "Major"]
        minor_arcana = [c for c in self._deck.cards if c.arcana == "Minor"]

        # Major Arcana
        if major_arcana:
            lines.append(f"MAJOR ARCANA ({len(major_arcana)} cards):")
            fool = next((c for c in major_arcana if c.number == 0), None)
            world = next((c for c in major_arcana if c.number == 21), None)
            if fool and world:
                lines.append(
                    f"  Special Pair: {fool.name} ({fool.number}) - {world.name} ({world.number})"
                )

            double_letter_trumps = [c for c in major_arcana if 3 <= c.number <= 21]
            lines.append(f"  Double Letter Trumps ({len(double_letter_trumps)} cards): Cards 3-21")
            lines.append("")

        # Minor Arcana
        if minor_arcana:
            lines.append(f"MINOR ARCANA ({len(minor_arcana)} cards - 4 suits × 14 ranks):")
            lines.append("")

            # Aces
            aces = [c for c in minor_arcana if hasattr(c, "pip") and c.pip == 1]
            if aces:
                lines.append(f"  ACES ({len(aces)} cards - The Root Powers):")
                for ace in aces:
                    suit_name = ace.suit.name if hasattr(ace.suit, "name") else str(ace.suit)
                    lines.append(f"    Ace of {suit_name}")
                lines.append("")

            # Pips (2-10)
            pips = [c for c in minor_arcana if hasattr(c, "pip") and 2 <= c.pip <= 10]
            if pips:
                lines.append(f"  PIPS ({len(pips)} cards - 2-10 of each suit):")
                # Group by suit
                suits_dict = {}
                for pip in pips:
                    suit_name = pip.suit.name if hasattr(pip.suit, "name") else str(pip.suit)
                    if suit_name not in suits_dict:
                        suits_dict[suit_name] = []
                    suits_dict[suit_name].append(pip)

                for suit_name in ["Cups", "Pentacles", "Swords", "Wands"]:
                    if suit_name in suits_dict:
                        pip_nums = sorted([p.pip for p in suits_dict[suit_name]])
                        lines.append(f"    {suit_name}: {', '.join(str(n) for n in pip_nums)}")
                lines.append("")

            # Court Cards
            courts = [c for c in minor_arcana if hasattr(c, "court_rank") and c.court_rank]
            if courts:
                lines.append(f"  COURT CARDS ({len(courts)} cards - 4 ranks × 4 suits):")
                # Get unique ranks and their order
                rank_order = {"Knight": 0, "Prince": 1, "Princess": 2, "Queen": 3}
                lines.append("    Rank order per suit: Knight, Prince, Princess, Queen")
                lines.append("")

                # Group by suit
                suits_dict = {}
                for court in courts:
                    suit_name = court.suit.name if hasattr(court.suit, "name") else str(court.suit)
                    if suit_name not in suits_dict:
                        suits_dict[suit_name] = []
                    suits_dict[suit_name].append(court)

                for suit_name in ["Cups", "Pentacles", "Swords", "Wands"]:
                    if suit_name in suits_dict:
                        suit_courts = sorted(
                            suits_dict[suit_name], key=lambda c: rank_order.get(c.court_rank, 99)
                        )
                        court_names = [c.court_rank for c in suit_courts]
                        lines.append(f"    {suit_name}: {', '.join(court_names)}")
                lines.append("")

        # Element correspondences
        lines.append("SUIT CORRESPONDENCES:")
        suits_info = {}
        for card in minor_arcana:
            if hasattr(card, "suit") and card.suit:
                suit_name = card.suit.name if hasattr(card.suit, "name") else str(card.suit)
                if suit_name not in suits_info:
                    # Extract element info
                    element_name = "Unknown"
                    if hasattr(card.suit, "element") and card.suit.element:
                        if hasattr(card.suit.element, "name"):
                            element_name = card.suit.element.name
                        else:
                            element_name = str(card.suit.element)

                    # Extract zodiac signs
                    zodiac_signs = []
                    if hasattr(card.suit, "element") and card.suit.element:
                        if hasattr(card.suit.element, "zodiac_signs"):
                            zodiac_signs = card.suit.element.zodiac_signs

                    # Extract keywords
                    keywords = []
                    if hasattr(card.suit, "element") and card.suit.element:
                        if hasattr(card.suit.element, "keywords"):
                            keywords = card.suit.element.keywords

                    suits_info[suit_name] = {
                        "element": element_name,
                        "zodiac": zodiac_signs,
                        "keywords": keywords,
                    }

        for suit_name in ["Cups", "Pentacles", "Swords", "Wands"]:
            if suit_name in suits_info:
                info = suits_info[suit_name]
                lines.append(f"  {suit_name} ({info['element']}):")
                if info["zodiac"]:
                    lines.append(f"    Zodiac: {', '.join(info['zodiac'])}")
                if info["keywords"]:
                    lines.append(f"    Keywords: {', '.join(info['keywords'])}")

        lines.append("")
        lines.append(f"Total: {len(self._deck.cards)} cards")

        return "\n".join(lines)

    def __repr__(self) -> str:
        """Return a nice representation of the deck accessor."""
        return self.__str__()

    def spread(self, spread_name: str):
        """
        Draw a Tarot card reading for a spread.

        Automatically draws random cards for each position in the spread,
        with random reversals. Returns formatted reading with card details.

        Args:
            spread_name: Name of the spread (case-insensitive, underscores or spaces)
                        Examples: 'Celtic Cross', 'golden dawn', 'three_card', 'tree of life'

        Returns:
            SpreadReading object containing the spread and drawn cards

        Raises:
            ValueError: If spread name not found

        Examples:
            print(Tarot.deck.card.spread('Celtic Cross'))
            print(Tarot.deck.card.spread('golden dawn'))
            print(Tarot.deck.card.spread('three card'))
            print(Tarot.deck.card.spread('tree of life'))
        """
        from tarot.card.spread import Spread, SpreadReading, draw_spread

        # Initialize deck if needed
        self._ensure_initialized()

        # Create spread object
        spread = Spread(spread_name)

        # Draw cards for the spread
        drawn_cards = draw_spread(spread, self._deck.cards if self._deck else None)

        # Create and return reading
        reading = SpreadReading(spread, drawn_cards)
        return reading
