"""
Generic registry mixin that gives any class hierarchy a per-subclass
name → instance registry with lookup, enumeration, and auto-resolve helpers.
"""

from abc import ABC, abstractmethod
from collections.abc import ItemsView
from functools import wraps
from typing import Any, ClassVar, Generic, TypeVar, cast

Tid = TypeVar("Tid")
Titem = TypeVar("Titem", bound="RegistryMixin[Any, Any]")


class RegistryMixin(Generic[Tid, Titem], ABC):
    """
    Abstract mixin providing a class-level ``_registry`` dictionary.

    Each concrete subclass gets its own isolated registry (via
    ``__init_subclass__``), so ``SubclassA.get_all_items()`` only returns
    items registered on ``SubclassA``, not those on ``SubclassB``.

    Subclasses must implement ``get_id`` and ``from_registry``.
    """

    _registry: ClassVar[dict[Any, Any]] = {}

    def __init_subclass__(cls, **kwargs: Any) -> None:
        """Give every subclass its own empty registry dict."""
        super().__init_subclass__(**kwargs)
        cls._registry = {}

    @abstractmethod
    def get_id(self) -> Tid:
        """Return the unique identifier for this instance."""
        ...

    @classmethod
    @abstractmethod
    def from_registry(cls, identifier: Tid | Titem) -> Titem:
        """Resolve *identifier* (an ID or an existing instance) to a registered item."""
        ...

    @classmethod
    def add_to_registry(cls, item_id: Tid, item: Titem) -> None:
        """Register *item* under *item_id* in this class's registry."""
        cls._registry[item_id] = item

    @classmethod
    def get_all_items(cls) -> list[Titem]:
        """Return all items registered on this specific class."""
        return list(cls._registry.values())

    @classmethod
    def get_registry_view(cls) -> ItemsView[Tid, Titem]:
        """Return an items view of this class's registry."""
        return cast(ItemsView[Tid, Titem], cls._registry.items())

    @classmethod
    def get_item(cls, item_id: Tid | None) -> Titem | None:
        """Look up a single item by *item_id*, returning ``None`` if not found."""
        if item_id is None:
            return None
        return cls._registry.get(item_id)

    @classmethod
    def unregister(cls, item: Titem) -> None:
        """Remove *item* from this class's registry."""
        cls._registry.pop(item.get_id(), None)

    @classmethod
    def auto_resolve(cls, func):
        """
        Decorator that resolves the first positional argument from an ID
        to a registered instance before calling *func*.
        """

        @wraps(func)
        def wrapper(*args, **kwargs):
            arg_list = list(args)
            if arg_list:
                arg_list[0] = cls.from_registry(arg_list[0])
            return func(*arg_list, **kwargs)

        return wrapper
