"""
Релизация генератора Адаптера для заданного интерфейса
"""
from abc import abstractmethod, ABCMeta
from typing import Type, Generator, cast
from inspect import signature, currentframe
from lesson3.interfaces import GenericInterface, UObject
from lesson14.interfaces import IoCController


class InterfaceToAdapterMeta(ABCMeta):
    """Метакласс для создания классов адаптеров. Использвать так:
    MyAdapter = class(MyInterface, metaclass=InterfaceToAdapterMeta)"""

    def __new__(mcs, name, bases, class_dict):
        def init(self, obj: UObject, ioc: IoCController):
            """Конструктор для экземпляра адаптера"""
            self.obj = obj
            self.ioc = ioc

        def ioc_call(class_name, method_name, bind_func):
            """Фабрика реализаций методов Адаптера. Осуществляет трансляцию
            вызова метода в соответствующий вызов ioc"""
            def call_imp(self, *args, **kwargs):
                """Реализация метода Адаптера"""
                obj = self.obj
                ioc = self.ioc
                bound_args = bind_func(obj, *args, **kwargs)
                bound_args.apply_defaults()
                rv = ioc.resolve(f'{class_name}:{method_name}',
                                 *bound_args.args, **bound_args.kwargs)
                return rv

            return call_imp

        interfaces = [x for x in bases if issubclass(x, GenericInterface)]
        if 1 < len(interfaces):
            raise NotImplementedError('Адаптер к множеству интерфейсов не поддерживается')
        elif len(interfaces) == 1:
            itf = interfaces[0]
            if not issubclass(itf, GenericInterface):
                raise ValueError(f'Интерфейс {bases[0]!s} должен быть наследником GenericInterface')
            itf_name = f'{itf.__module__}.{itf.__name__}'
            methods = set()
            # call_stack = currentframe()
            # while call_stack.f_globals['__name__'] == __name__:
            #     call_stack = call_stack.f_back
            # itf_module = call_stack.f_locals
            # del call_stack
            for f_name, f_body in mcs.methods_enum(itf):
                class_dict[f_name] = ioc_call(itf_name, f_name, signature(f_body).bind)
                methods.add(f_name)
            class_dict['_assert_support'] = mcs.support_func(methods)
            class_dict['__init__'] = init
        rv = super().__new__(mcs, name, bases, class_dict)
        return rv

    @staticmethod
    def support_func(methods: set) -> callable:
        """Проверка поддержки интерфейса Объектом"""
        def support_imp(obj):
            """Реализация контроля поддержки"""
            for name in methods:
                if not callable(getattr(obj, name)):
                    GenericInterface._not_supported_error(obj)

        return support_imp

    @staticmethod
    def methods_enum(child: Type[GenericInterface]) -> Generator:
        """Найти все методы из GenericInterface"""
        parent_list = child.__mro__
        for parent in parent_list:
            if not issubclass(parent, GenericInterface):
                continue
            for f_name, f_body in parent.__dict__.items():
                if f_name.startswith('_') or not callable(f_body):
                    continue
                yield f_name, f_body
        return


class InterfaceToAdapter(metaclass=InterfaceToAdapterMeta):
    """"Сахар" для InterfaceToAdapterMeta. Использовать так:
    MyAdapter = class(MyInterface, InterfaceToAdapter)"""
    def __init__(self, _: UObject, __: IoCController):
        """Псевдо-конструктор. Будет заменен конструктором из МетаКласса"""


def interface_to_adapter(itf: Type[GenericInterface], obj: UObject,
                         ioc: IoCController) -> GenericInterface:
    """Метод для создания Адаптеров из Итерфейсов"""
    itf_name = itf.__name__
    ada_name = f'Autogenerated{itf_name}Adapter'
    ada_class = cast(Type[InterfaceToAdapter],
                     type(ada_name, (itf, InterfaceToAdapter), {}))
    ada_imp = cast(GenericInterface, ada_class(obj, ioc))
    return ada_imp


def adapter_factory_register(ioc: IoCController) -> None:
    """Регистрация фабрики Адаптеров. Регистрируется метод
    Adapter(itf_class, u_object) -> adapter_instance
    """
    ioc.resolve('ioc.register', 'Adapter', interface_to_adapter)
