"""
Регистр функций (getters, handlers, conditions, validators)
"""

from typing import Any
from collections.abc import Callable
from importlib import import_module

from .exceptions import ConfigurationError, RegistryError


class FunctionRegistry:
    """Регистр функций"""

    def __init__(self) -> None:
        self._functions: dict[str, Callable] = {}

    def register(self, name: str, func: Callable) -> None:
        """Зарегистрировать функцию"""
        self._functions[name] = func

    def register_decorator(self, name: str) -> Callable:
        """Декоратор для регистрации"""

        def decorator(func: Callable) -> Callable:
            self.register(name, func)
            return func

        return decorator

    def get(self, name: str) -> Callable:
        """Получить функцию по имени"""
        if name not in self._functions:
            raise RegistryError(name, "function")
        return self._functions[name]

    def has(self, name: str) -> bool:
        """Проверить наличие функции"""
        return name in self._functions

    def list_registered(self) -> list[str]:
        """Список всех зарегистрированных функций"""
        return list(self._functions.keys())

    def load_from_path(self, name: str, import_path: str) -> None:
        """
        Загрузить функцию из модуля
        import_path: 'module.submodule.function_name'
        """
        parts = import_path.rsplit(".", 1)
        if len(parts) != 2:
            msg = f"Invalid import path: {import_path}"
            raise ConfigurationError(msg)

        module_path, func_name = parts
        module = import_module(module_path)
        func = getattr(module, func_name)

        self.register(name, func)

    def load_batch(self, functions_map: dict[str, str]) -> None:
        """
        Загрузить несколько функций
        functions_map: {'name': 'module.path.function'}
        """
        for name, import_path in functions_map.items():
            self.load_from_path(name, import_path)

    async def call(self, name: str, *args: Any, **kwargs: Any) -> Any:
        """Вызвать функцию"""
        func = self.get(name)

        if callable(func):
            result = func(*args, **kwargs)
            if hasattr(result, "__await__"):
                return await result
            return result

        return func(*args, **kwargs)


class ConditionRegistry(FunctionRegistry):
    """Регистр функций-условий (должны возвращать bool)"""

    async def check(self, name: str, context: Any) -> bool:
        """
        Проверить условие
        Функция должна принимать context и возвращать bool
        """
        result = await self.call(name, context)
        if not isinstance(result, bool):
            raise RegistryError(name, f"condition (must return bool, got {type(result).__name__})")
        return result

    async def check_all(self, names: list[str], context: Any) -> bool:
        """Проверить все условия (AND)"""
        for name in names:
            if not await self.check(name, context):
                return False
        return True


class GetterRegistry(FunctionRegistry):
    """Регистр функций-геттеров для флагов"""

    async def get_value(self, name: str, context: Any) -> Any:
        """Получить значение через геттер"""
        return await self.call(name, context)


class ValidatorRegistry(FunctionRegistry):
    """Регистр функций-валидаторов для ввода"""

    async def validate(self, name: str, value: Any, context: Any) -> tuple[bool, str | None]:
        """
        Валидировать значение
        Функция должна возвращать (is_valid, error_message)
        """
        result = await self.call(name, value, context)

        if not isinstance(result, tuple) or len(result) != 2:
            raise RegistryError(name, "validator (must return (bool, str|None))")

        return result


class ActionRegistry(FunctionRegistry):
    """Регистр кастомных действий"""

    async def execute(self, name: str, context: Any, params: dict[str, Any]) -> Any:
        """Выполнить кастомное действие"""
        return await self.call(name, context, params)


class BusinessActionsRegistry(FunctionRegistry):
    """
    Регистр бизнес-действий для ActionType.BUSINESS_LOGIC

    Функции должны иметь сигнатуру:
        async def my_action(context: ExecutionContext, event: Message | CallbackQuery, **params) -> Any
    """

    async def execute(
        self,
        action_name: str,
        context: Any,
        event: Any,
        params: dict[str, Any] | None = None,
    ) -> Any:
        """Выполнить бизнес-действие"""
        if action_name not in self._functions:
            raise RegistryError(action_name, "business_action")

        func = self._functions[action_name]

        if params:
            return await func(context, event, **params)
        return await func(context, event)


class MasterRegistry:
    """Мастер-регистр, объединяющий все регистры"""

    def __init__(self) -> None:
        self.conditions = ConditionRegistry()
        self.getters = GetterRegistry()
        self.validators = ValidatorRegistry()
        self.actions = ActionRegistry()
        self.business_actions = BusinessActionsRegistry()

    def load_from_schema(
        self, custom_functions: dict[str, str], business_actions: dict[str, str] | None = None
    ) -> None:
        """
        Загрузить функции из схемы UIRouter
        """
        for name, import_path in custom_functions.items():
            if name.startswith("get_") or name.endswith("_getter"):
                self.getters.load_from_path(name, import_path)
            elif name.startswith("validate_") or name.endswith("_validator"):
                self.validators.load_from_path(name, import_path)
            elif name.startswith(("is_", "has_")) or name.endswith("_condition"):
                self.conditions.load_from_path(name, import_path)
            elif name.startswith("action_") or name.endswith("_action"):
                self.actions.load_from_path(name, import_path)
            else:
                self.getters.load_from_path(name, import_path)

        if business_actions:
            for name, import_path in business_actions.items():
                self.business_actions.load_from_path(name, import_path)
