"""
Tarot Cube of Space module.

Provides hierarchical access to Cube > Wall > Direction structure.

Usage:
    from tarot.cube import Cube

    # Access walls
    Tarot.cube.wall("North")                          # Get specific wall
    Tarot.cube.wall().filter(element="Air")          # Filter all walls

    # Access directions (NEW - replaces old "area" concept)
    wall = Tarot.cube.wall("North")
    wall.filter("East")                               # Filter by direction
    wall.filter(element="Fire")                       # Filter by attribute
    wall.direction("East")                            # Get specific direction
"""

from typing import TYPE_CHECKING, Any, Optional

if TYPE_CHECKING:
    from kaballah.cube.attributes import CubeOfSpace


class CubeMeta(type):
    """Metaclass to add __str__ to Cube class itself."""

    def __str__(cls) -> str:
        """Return readable representation when Cube is converted to string."""
        cls._ensure_initialized()
        if cls._cube is None:
            return "Cube of Space (not initialized)"

        walls = cls._cube.walls if hasattr(cls._cube, "walls") else {}
        lines = [
            "Cube of Space",
            "=" * 60,
            f"Walls: {len(walls)} (North, South, East, West, Above, Below)",
            "",
            "Structure:",
        ]

        # Show walls with their elements and areas
        for wall_name in ["North", "South", "East", "West", "Above", "Below"]:
            wall = walls.get(wall_name)
            if wall:
                element = f" [{wall.element}]" if hasattr(wall, "element") else ""
                areas = len(wall.directions) if hasattr(wall, "directions") else 0
                lines.append(f"  {wall_name}{element}: {areas} areas")

        return "\n".join(lines)

    def __repr__(cls) -> str:
        """Return object representation."""
        cls._ensure_initialized()
        if cls._cube is None:
            return "Cube(not initialized)"
        walls = cls._cube.walls if hasattr(cls._cube, "walls") else {}
        return f"Cube(walls={len(walls)})"


class DirectionAccessor:
    """Fluent accessor for filtering and accessing directions within a specific wall."""

    _wall: Optional[Any] = None

    def __init__(self, wall: Any):
        """Initialize with a Wall object."""
        self._wall = wall

    def all(self) -> list:
        """Get all directions in this wall."""
        if self._wall is None or not hasattr(self._wall, "directions"):
            return []
        return list(self._wall.directions.values())

    def filter(self, direction_name: Optional[str] = None, **kwargs) -> list:
        """
        Filter directions in this wall by name or any WallDirection attribute.

        Args:
            direction_name: Specific direction (North, South, East, West, Center)
            **kwargs: Any WallDirection attribute

        Returns:
            List of WallDirection objects matching filters
        """
        from utils.filter import universal_filter

        all_dirs = self.all()

        # Filter by direction name if provided
        if direction_name:
            all_dirs = [d for d in all_dirs if d.name.lower() == direction_name.lower()]

        # Apply other filters
        if kwargs:
            all_dirs = universal_filter(all_dirs, **kwargs)

        return all_dirs

    def display(self) -> str:
        """Display all directions in this wall formatted."""
        from utils.filter import format_results

        return format_results(self.all())

    def display_filter(self, direction_name: Optional[str] = None, **kwargs) -> str:
        """
        Filter directions and display results nicely formatted.

        Args:
            direction_name: Direction name to filter
            **kwargs: Any WallDirection attribute

        Example:
            print(wall.display_filter("East"))
        """
        from utils.filter import format_results

        results = self.filter(direction_name, **kwargs)
        return format_results(results)

    def __call__(self, direction_name: Optional[str] = None) -> Optional[Any]:
        """Get specific direction by name."""
        if direction_name is None:
            return self.all()
        if self._wall is None or not hasattr(self._wall, "directions"):
            return None
        return self._wall.directions.get(direction_name.capitalize())

    def __repr__(self) -> str:
        """Return friendly representation."""
        directions = self.all()
        dir_names = ", ".join([d.name for d in directions])
        wall_name = self._wall.name if self._wall else "Unknown"
        return f"DirectionAccessor({wall_name}: {dir_names})"

    def __str__(self) -> str:
        """Return formatted string of all directions."""
        return self.display()


class WallWrapper:
    """Wraps a Wall object to add DirectionAccessor for hierarchical access."""

    def __init__(self, wall: Any):
        """Initialize with a Wall object."""
        self._wall = wall
        self._direction_accessor = DirectionAccessor(wall)

    def __getattr__(self, name: str) -> Any:
        """Delegate attribute access to the wrapped wall."""
        if name in ("_wall", "_direction_accessor"):
            return object.__getattribute__(self, name)
        return getattr(self._wall, name)

    def filter(self, direction_name: Optional[str] = None, **kwargs) -> list:
        """
        Filter directions in this wall.

        Usage:
            wall.filter("East")                 # Get East direction
            wall.filter(element="Fire")         # Get Fire directions
        """
        return self._direction_accessor.filter(direction_name, **kwargs)

    def display_filter(self, direction_name: Optional[str] = None, **kwargs) -> str:
        """
        Filter directions and display results nicely formatted.

        Usage:
            wall.display_filter("East")
            wall.display_filter(element="Fire")
        """
        return self._direction_accessor.display_filter(direction_name, **kwargs)

    def direction(self, direction_name: str) -> Optional[Any]:
        """Get a specific direction."""
        return self._direction_accessor(direction_name)

    def all_directions(self) -> list:
        """Get all directions in this wall."""
        return self._direction_accessor.all()

    def __repr__(self) -> str:
        """Return friendly representation."""
        return f"Wall({self._wall.name}, {self._wall.element})"

    def __str__(self) -> str:
        """Return formatted string of wall details."""
        wall = self._wall
        lines = [
            f"--- {wall.name} ---",
            f"  name: {wall.name}",
            f"  side: {wall.side}",
            f"  element: {wall.element}",
            f"  planet: {wall.planet}",
            f"  opposite: {wall.opposite}",
            f"  archangel: {wall.archangel}",
            f"  keywords: {', '.join(wall.keywords) if wall.keywords else 'None'}",
            f"  description: {wall.description}",
            f"  directions: {', '.join(wall.directions.keys())}",
        ]
        return "\n".join(lines)


class WallAccessor:
    """Fluent accessor for filtering and accessing Cube walls."""

    _cube: Optional["CubeOfSpace"] = None  # type: ignore
    _initialized: bool = False

    @classmethod
    def _ensure_initialized(cls) -> None:
        """Lazy-initialize the Cube on first access."""
        if cls._initialized:
            return
        from kaballah.cube.attributes import CubeOfSpace

        WallAccessor._cube = CubeOfSpace.create_default()
        WallAccessor._initialized = True

    def all(self) -> list:
        """Get all walls."""
        self._ensure_initialized()
        if WallAccessor._cube is None:
            return []
        return WallAccessor._cube.all_walls()

    def filter(self, **kwargs) -> list:
        """
        Filter walls by any Wall attribute.

        Uses the universal filter for consistency across the project.

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

        Usage:
            Cube.wall.filter(element="Air")
            Cube.wall.filter(planet="Mercury")

        Returns:
            List of Wall objects matching all filters
        """
        from utils.filter import universal_filter

        return universal_filter(self.all(), **kwargs)

    def display(self) -> str:
        """Display all walls formatted."""
        from utils.filter import format_results

        return format_results(self.all())

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

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

        Returns:
            Formatted string with filtered walls

        Example:
            print(Cube.wall.display_filter(element="Air"))
        """
        results = self.filter(**kwargs)
        # Use the custom __str__ method of Wall objects for proper line-by-line formatting
        return "\n\n".join([str(wall) for wall in results])

    def __call__(self, wall_name: Optional[str] = None) -> Optional[Any]:
        """Get a specific wall by name or return all walls.

        Deprecated: Use filter(side="north") instead.
        """
        self._ensure_initialized()

        if wall_name is None:
            return self.all()

        if WallAccessor._cube is None:
            return None
        return WallAccessor._cube.wall(wall_name)

    def __repr__(self) -> str:
        """Return friendly representation showing all walls."""
        walls = self.all()
        wall_names = ", ".join([w.name for w in walls])
        return f"WallAccessor({wall_names})"

    def __str__(self) -> str:
        """Return formatted string of all walls."""
        return self.display()


class Cube(metaclass=CubeMeta):
    """
    Unified accessor for Cube of Space correspondences.

    Hierarchical structure: Cube > Wall > Direction

    Usage:
        # Filter walls by side
        north = Cube.wall.filter(side="north")[0]           # Get north wall
        air_walls = Cube.wall.filter(element="Air")         # Filter by element

        # Access all walls
        all_walls = Cube.wall.all()                         # Get all 6 walls

        # Work with directions within a wall
        wall = Cube.wall.filter(side="north")[0]
        east_dir = wall.direction("East")                   # Get direction
        fire_dirs = wall.filter(element="Fire")             # Filter directions
    """

    _cube: Optional["CubeOfSpace"] = None  # type: ignore
    _initialized: bool = False
    _wall_accessor: Optional[WallAccessor] = None

    @classmethod
    def _ensure_initialized(cls) -> None:
        """Lazy-initialize the Cube of Space on first access."""
        if cls._initialized:
            return

        from kaballah.cube.attributes import CubeOfSpace

        cls._cube = CubeOfSpace.create_default()
        cls._initialized = True

    @classmethod
    def _get_wall_accessor(cls) -> "WallAccessor":
        """Get or create the wall accessor."""
        cls._ensure_initialized()
        if cls._wall_accessor is None:
            cls._wall_accessor = WallAccessor()
        return cls._wall_accessor

    # Use a descriptor to make wall work like a property on the class
    class WallProperty:
        """Descriptor that returns wall accessor when accessed."""

        def __get__(self, obj: Any, objtype: Optional[type] = None) -> "WallAccessor":
            if objtype is None:
                objtype = type(obj)
            return objtype._get_wall_accessor()

    wall = WallProperty()

    @classmethod
    def opposite_wall(cls, wall_name: str) -> Optional[object]:
        """Get the opposite wall."""
        cls._ensure_initialized()
        if cls._cube is None:
            return None
        return cls._cube.opposite_wall(wall_name)
