import json
from abc import ABC, abstractmethod
from typing import Any, Callable

from .exceptions import NonConvertibleValue

__all__ = ["BaseConverter", "Bool", "Tuple"]


class BaseConverter(ABC):
    def __call__(self, value: str) -> Any:
        try:
            return self._convert(value)
        except Exception:
            raise NonConvertibleValue(value)

    @abstractmethod
    def _convert(self, value: str) -> Any:
        raise NotImplementedError()


class Bool(BaseConverter):
    """Конвертирует строку в булевое значение."""

    def _convert(self, value: str) -> bool:
        return bool(json.loads(value))


class Tuple(BaseConverter):
    """Конвертирует строку с разделителем в кортеж. Для каждого элемента используется
    указанный ковертер.

    :param element_converter: любой callable, который принимает строку и возвращает
    значение желаемого типа
    :param delimiter: разделитель элементов в строке
    """

    __slots__ = "element_converter", "delimiter"

    element_converter: Callable[[str], Any]
    delimiter: str

    def __init__(
        self, element_converter: Callable[[str], Any] = str, *, delimiter: str = ","
    ):
        self.element_converter = element_converter
        self.delimiter = delimiter

    def _convert(self, value: str) -> tuple:
        return tuple(
            self.element_converter(el.strip()) for el in value.split(self.delimiter)
        )
