"""
Tarot Tree of Life module.

Provides access to Sephiroth, Paths, and Tree of Life correspondences.

Usage:
    from tarot.tree import Tree

    sephera = Tree.sephera(1)                       # Get Sephira 1 (Kether)
    path = Tree.path(11)                            # Get Path 11
    all_sepheras = Tree.sephera()                   # Get all Sephiroth

    print(Tree())                                   # Display Tree structure
"""

from typing import TYPE_CHECKING, Dict, Optional, Union, overload

if TYPE_CHECKING:
    from tarot.attributes import Path, Sephera
    from tarot.card.data import CardDataLoader
    from utils.query import Query


class TreeMeta(type):
    """Metaclass to add __str__ to Tree class itself."""

    def __str__(cls) -> str:
        """Return readable representation when Tree is converted to string."""
        # Access Tree class attributes through type.__getattribute__
        Tree._ensure_initialized()
        sepheras = type.__getattribute__(cls, "_sepheras")
        paths = type.__getattribute__(cls, "_paths")
        lines = [
            "Tree of Life",
            "=" * 60,
            f"Sephiroth: {len(sepheras)} nodes",
            f"Paths: {len(paths)} connections",
            "",
            "Structure:",
        ]

        # Show Sephira hierarchy
        for num in sorted(sepheras.keys()):
            seph = sepheras[num]
            lines.append(f"  {num}. {seph.name} ({seph.hebrew_name})")

        return "\n".join(lines)

    def __repr__(cls) -> str:
        """Return object representation."""
        Tree._ensure_initialized()
        sepheras = type.__getattribute__(cls, "_sepheras")
        paths = type.__getattribute__(cls, "_paths")
        return f"Tree(sepheras={len(sepheras)}, paths={len(paths)})"


class Tree(metaclass=TreeMeta):
    """
    Unified accessor for Tree of Life correspondences.

    All methods are class methods, so Tree is accessed as a static namespace:

        sephera = Tree.sephera(1)
        path = Tree.path(11)
        print(Tree())  # Displays tree structure
    """

    _sepheras: Dict[int, "Sephera"] = {}  # type: ignore
    _paths: Dict[int, "Path"] = {}  # type: ignore
    _initialized: bool = False
    _loader: Optional["CardDataLoader"] = None  # type: ignore

    @classmethod
    def _ensure_initialized(cls) -> None:
        """Lazy-load data from CardDataLoader on first access."""
        if cls._initialized:
            return

        from tarot.card.data import CardDataLoader

        cls._loader = CardDataLoader()
        cls._sepheras = cls._loader._sephera
        cls._paths = cls._loader._paths
        cls._initialized = True

    @classmethod
    @overload
    def sephera(cls, number: int) -> Optional["Sephera"]: ...

    @classmethod
    @overload
    def sephera(cls, number: None = ...) -> Dict[int, "Sephera"]: ...

    @classmethod
    def sephera(
        cls, number: Optional[int] = None
    ) -> Union[Optional["Sephera"], Dict[int, "Sephera"]]:
        """Return a Sephira or all Sephiroth."""
        cls._ensure_initialized()
        if number is None:
            return cls._sepheras.copy()
        return cls._sepheras.get(number)

    @classmethod
    @overload
    def path(cls, number: int) -> Optional["Path"]: ...

    @classmethod
    @overload
    def path(cls, number: None = ...) -> Dict[int, "Path"]: ...

    @classmethod
    def path(cls, number: Optional[int] = None) -> Union[Optional["Path"], Dict[int, "Path"]]:
        """Return a Path or all Paths."""
        cls._ensure_initialized()
        if number is None:
            return cls._paths.copy()
        return cls._paths.get(number)

    @classmethod
    def filter(cls, expression: str) -> "Query":
        """
        Filter Sephiroth by attribute:value expression.

        Examples:
            Tree.filter('name:Kether').first()
            Tree.filter('number:1').first()
            Tree.filter('sphere:1').all()

        Returns a Query object for chaining.
        """
        from tarot.query import Query

        cls._ensure_initialized()
        # Create a query from all Sephiroth
        return Query(cls._sepheras).filter(expression)
