# coding: utf-8
"""\
Описание различных обёрток над типами, для выделения из конфигураций параметров со сложными типами
"""

from typing import TypeVar, Type, Any, List, Iterable, Dict, Union, Callable


T = TypeVar('T')
K = TypeVar('K')


class BaseTypeHelper(object):
    """\
    Базовый класс для типов-обёрток
    """
    __name__ = 'aw_config.type_helpers.BaseTypeHelper'

    def __call__(self, val: Any):
        raise NotImplemented()


class ANY(BaseTypeHelper):
    """\
    Класс не проводящий никакой обработки содержимого
    """
    __name__ = 'aw_config.type_helpers.ANY'

    def __call__(self, val: Any) -> Any:
        return val


class LIST(BaseTypeHelper):
    """\
    Преобразуем параметр в список нужного типа
    """
    __name__ = 'aw_config.type_helpers.LIST'

    def __init__(self, el_type: Union[Type[T], Callable[[Any], T]]):
        self.el_type = el_type

    def __repr__(self) -> str:
        return f"<class '{type(self).__name__}[{repr(self.el_type)}]'>"

    def __call__(self, val: Iterable) -> List[T]:
        res = []
        for i in val:
            try:
                res.append(self.el_type(i))

            except (ValueError, TypeError) as e:
                raise ValueError(f'Не удалось привести значение "{i}" к типу "{repr(self.el_type)}" '
                                 f'в составе списка. Ошибка: {e}')

        return res


class DICT(BaseTypeHelper):
    """\
    Преобразуем параметр в словарь, с нужными типами ключа и значения
    """
    __name__ = 'aw_config.type_helpers.DICT'

    def __init__(self, key_type: Union[Type[K], Callable[[Any], K]], el_type: Union[Type[T], Callable[[Any], T]]):
        self.el_type = el_type
        self.k_type = key_type

    def __repr__(self) -> str:
        return f"<class '{type(self).__name__}[{repr(self.k_type)}, {repr(self.el_type)}]'>"

    def __call__(self, val: Dict) -> Dict[K, T]:
        res = dict()
        for k, v in val.items():
            try:
                _k = self.k_type(k)

            except (ValueError, TypeError) as e:
                raise ValueError(f'Ключ "{k}" не может быть преобразован в тип {repr(self.k_type)} '
                                 f'для комбинации ключ-значение: "{k}", "{v}" '
                                 f'Ошибка: "{e}"')

            try:
                _v = self.el_type(v)

            except (ValueError, TypeError) as e:
                raise ValueError(f'Значение "{v}" не может быть преобразован в тип {repr(self.el_type)} '
                                 f'для комбинации ключ-значение: "{k}", "{v}" '
                                 f'Ошибка: "{e}"')

            res[_k] = _v

        return res
