"""Contains callback handling routines."""
from __future__ import annotations

import typing


class _CallbackCallWrapper:
    def __init__(self, handler: CallbackHandler, name: str):
        self._name = name
        self._handler = handler

    def __call__(self, *args: tuple, **kwargs: dict) -> typing.Any:
        return self.handler._call(self.name, *args, **kwargs)  # noqa: SLF001

    @property
    def name(self) -> str:
        """Get the callback name."""
        return self._name

    @property
    def handler(self) -> CallbackHandler:
        """Get the handler for the callback."""
        return self._handler


class CallbackHandler:
    """Handler for callbacks for HSMS/SECS/GEM events.

    This handler manages callbacks for events that can happen on a handler for a connection.
    """

    def __init__(self) -> None:
        """Initialize the handler."""
        self._callbacks: dict[str, typing.Callable] = {}
        self.target: object = None
        self._object_intitialized = True

    def __setattr__(self, name: str, value: typing.Callable):
        """Set an item as object member.

        Args:
            name: name of the callback
            value: callback function

        """
        if "_object_intitialized" not in self.__dict__ or name in self.__dict__:
            dict.__setattr__(self, name, value)
            return

        if value is None:
            if name in self._callbacks:
                del self._callbacks[name]
        else:
            self._callbacks[name] = value

    def __getattr__(self, name: str) -> typing.Callable:
        """Get a callable function for an event.

        Args:
            name: name of the event

        """
        return _CallbackCallWrapper(self, name)

    class _CallbacksIter:
        def __init__(self, keys):
            self._keys = list(keys)
            self._counter = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self._counter < len(self._keys):
                i = self._counter
                self._counter += 1
                return self._keys[i]

            raise StopIteration

    def __iter__(self) -> _CallbacksIter:
        """Get an iterator for the callbacks.

        Returns:
            callback iterator

        """
        return self._CallbacksIter(self._callbacks.keys())

    def __contains__(self, callback: str) -> bool:
        """Check if a callback is present.

        Args:
            callback: name of the event

        Returns:
            True if callback present

        """
        if callback in self._callbacks:
            return True

        delegate_handler = getattr(self.target, "_on_" + callback, None)
        if callable(delegate_handler):
            return True

        return False

    def _call(self, callback: str, *args, **kwargs) -> typing.Any:
        if callback in self._callbacks:
            return self._callbacks[callback](*args, **kwargs)

        delegate_handler = getattr(self.target, "_on_" + callback, None)
        if callable(delegate_handler):
            return delegate_handler(*args, **kwargs)

        return None
