"""Оптимизированные метаклассы для одновременной работы как с экземпляром, так и с классом."""

from abc import ABCMeta
from functools import wraps
from typing import Any, Callable, ItemsView, Iterator, KeysView, Type, ValuesView


class _AxiReprMeta(ABCMeta):
    def __repr__(cls) -> str:
        return f"<class 'axipy.{cls.__name__}'>"


# collections abc meta


class _SizedMeta(ABCMeta):

    # @abstractmethod
    def __len__(cls) -> int:
        raise NotImplementedError


class _IterableMeta(ABCMeta):

    # @abstractmethod
    def __iter__(cls) -> Iterator:
        raise NotImplementedError


class _ContainerMeta(ABCMeta):

    # @abstractmethod
    def __contains__(cls, item: Any) -> bool:
        raise NotImplementedError


# Mixin
class _CollectionMeta(_SizedMeta, _IterableMeta, _ContainerMeta): ...


class _MappingMeta(_CollectionMeta):

    # @abstractmethod
    def __getitem__(cls, key: Any) -> Any:
        raise NotImplementedError

    # Default
    def __contains__(cls, key: Any) -> Any:
        try:
            cls[key]
        except KeyError:
            return False
        else:
            return True

    def _keys(cls) -> list:
        return list(KeysView(cls))  # type: ignore[arg-type]

    def _values(cls) -> list:
        return list(ValuesView(cls))  # type: ignore[arg-type]

    def _items(cls) -> list:
        return list(ItemsView(cls))  # type: ignore[arg-type]

    def _get(cls, key: Any, default: Any = None) -> Any:
        try:
            return cls[key]
        except KeyError:
            return default

    # def __eq__(cls, other) -> bool:
    #     if not isinstance(other, Mapping):
    #         return False
    #     return dict(cls.items()) == dict(other.items())


# Mixin
class _MappingMetaExtended(_MappingMeta, _AxiReprMeta): ...


def _decorate(cls: Type, func: Callable) -> Callable:

    @wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:

        if len(args) > 0 and isinstance(args[0], cls):
            result = func(*args, **kwargs)
        else:
            result = getattr(type(cls), "_" + func.__name__)(cls, *args, **kwargs)

        return result

    return wrapper
