"""Base class for everything in the world."""

from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field


@dataclass(eq=False)
class Object:
    """Base class for everything in the world.

    Every object has a location (another Object, or None for top-level things
    like zones). The location pointer is the entire containment system —
    contents is just the reverse lookup.
    """

    name: str
    location: Object | None = None
    x: int | None = None
    y: int | None = None

    _contents: list[Object] = field(
        default_factory=list, init=False, repr=False, compare=False
    )
    _verbs: dict[str, Callable[..., Awaitable[None]]] = field(
        default_factory=dict, init=False, repr=False, compare=False
    )

    def __post_init__(self) -> None:
        if self.location is not None:
            self.location._contents.append(self)

        # Auto-register verbs from @verb decorated methods
        for attr_name in dir(self):
            # Skip private/magic attributes
            if attr_name.startswith("_"):
                continue
            attr = getattr(self, attr_name)
            # Check if this is a verb-decorated method
            if hasattr(attr, "_verb_name"):
                self.register_verb(attr._verb_name, attr)

    @property
    def contents(self) -> list[Object]:
        """Everything whose location is this object."""
        return list(self._contents)

    def move_to(
        self,
        destination: Object | None,
        *,
        x: int | None = None,
        y: int | None = None,
    ) -> None:
        """Move this object to a new location.

        Removes from old location's contents, updates the location pointer,
        and adds to new location's contents. Coordinates are set from the
        keyword arguments (cleared to None if not provided).
        """
        # Remove from old location
        if self.location is not None and self in self.location._contents:
            self.location._contents.remove(self)

        # Update location and coordinates
        self.location = destination
        self.x = x
        self.y = y

        # Add to new location
        if destination is not None:
            destination._contents.append(self)

    def can_accept(self, obj: Object) -> bool:
        """Whether this object accepts obj as contents. Default: no."""
        return False

    def register_verb(self, name: str, handler: Callable[..., Awaitable[None]]) -> None:
        """Register a verb handler on this object."""
        self._verbs[name] = handler

    def get_verb(self, name: str) -> Callable[..., Awaitable[None]] | None:
        """Get a verb handler by name, or None if not found."""
        return self._verbs.get(name)

    def has_verb(self, name: str) -> bool:
        """Check if this object has a verb registered."""
        return name in self._verbs
