"""
Event Dispatcher для обработки пользовательских событий

Отвечает за:
- Поиск обработчиков события
- Проверку условий
- Выполнение actions через ActionExecutor
- Восстановление контекста пользователя
"""

import logging
from typing import TYPE_CHECKING, Any
from collections.abc import Callable, Awaitable

from aiogram import Bot

from .bus import EventData, EventSourceType
from ui_router.state.context import ExecutionContext, NavigationState, EventMode, NavigationManager
from ui_router.rules.engine import ConditionEvaluator

if TYPE_CHECKING:
    from ui_router.execution.actions import ActionExecutor
    from ui_router.state.factory import ContextFactory
    from ui_router.schema import UIRouter
    from ui_router.rules.flags import FlagResolver
    from ui_router.state.variables import VariableRepository
    from ui_router.rules.engine import RuleEngine


logger = logging.getLogger(__name__)


class EventDispatcher:
    """
    Обработчик событий с контекстом пользователя.

    Обрабатывает события типа INTERNAL, WEBHOOK, SCHEDULED.
    Требует валидный контекст пользователя для выполнения actions.
    """

    def __init__(
        self,
        schema: "UIRouter",
        navigation_manager: NavigationManager,
        action_executor: "ActionExecutor",
        flag_resolver: "FlagResolver",
        variable_repository: "VariableRepository",
        rule_engine: "RuleEngine",
        context_factory: "ContextFactory",
        bot_resolver: Callable[[int | str], Awaitable[Bot]] | None = None,
    ) -> None:
        """
        Инициализировать EventDispatcher

        Args:
            schema: UIRouter схема
            navigation_manager: NavigationManager для управления навигацией
            action_executor: ActionExecutor для выполнения actions
            flag_resolver: FlagResolver для резолва флагов
            variable_repository: VariableRepository для переменных
            rule_engine: RuleEngine для условий
            context_factory: ContextFactory для создания контекста
            bot_resolver: Резолвер для получения Bot по ID
        """
        self.schema = schema
        self.navigation_manager = navigation_manager
        self.action_executor = action_executor
        self.flag_resolver = flag_resolver
        self.variable_repository = variable_repository
        self.rule_engine = rule_engine
        self.context_factory = context_factory
        self.bot_resolver = bot_resolver

    async def dispatch(
        self,
        event_data: EventData,
        bot: Bot | None = None,
    ) -> None:
        """
        Обработать событие

        Args:
            event_data: Данные события
            bot: Bot instance (может быть None для фоновых задач без отправки сообщений)
        """
        if event_data.source_type in {EventSourceType.INTERNAL, EventSourceType.WEBHOOK} and not event_data.user_id:
            logger.warning(
                "Event '%s' (source: %s) has no user_id, skipping dispatch",
                event_data.event_name,
                event_data.source_type,
            )
            return

        handlers = [h for h in self.schema.event_handlers if h.event_name == event_data.event_name]

        if not handlers:
            logger.debug("No handlers for event '%s'", event_data.event_name)
            return

        handlers = sorted(handlers, key=lambda h: h.priority, reverse=True)

        for handler in handlers:
            try:
                event_mode = self._get_event_mode(event_data.source_type)

                if bot is None and self.bot_resolver:
                    bot = await self.bot_resolver(event_data.bot_id)

                nav_state = await self._get_navigation_state(event_data, bot)

                context = await self.context_factory.create_context(
                    navigation_state=nav_state,
                    scene_id=nav_state.current_scene,
                    bot=bot,
                    user_id=event_data.user_id,
                    chat_id=event_data.chat_id or event_data.user_id,
                    event=None,
                    event_mode=event_mode,
                    event_data={
                        **event_data.data,
                        "event_name": event_data.event_name,
                        "event_source": event_data.source_type,
                        "bot_id": event_data.bot_id,
                    },
                )

                for key, value in event_data.data.items():
                    context.set_flag(key, value)

                if not await self._check_conditions(handler, context):
                    logger.debug("Handler conditions not met for event '%s'", event_data.event_name)
                    continue

                await self._execute_actions(handler, context)

                try:
                    await context.save_to_storage()
                except Exception:
                    logger.exception("Failed to save navigation state for event '%s'", event_data.event_name)

                logger.debug("✅ Event '%s' handled successfully", event_data.event_name)

            except Exception:
                logger.exception("Error handling event '%s'", event_data.event_name)

    async def _get_navigation_state(self, event_data: EventData, bot: Bot | None) -> NavigationState:
        """
        Получить или создать навигационное состояние

        Args:
            event_data: Данные события
            bot: Bot instance

        Returns:
            NavigationState
        """
        if not event_data.user_id:
            return NavigationState(current_scene=self.schema.initial_scene)

        bot_id = bot.id if bot else event_data.bot_id

        nav_state = await self.navigation_manager.get_state(
            bot_id,
            event_data.user_id,
            event_data.chat_id or event_data.user_id,
        )

        if not nav_state or not nav_state.current_scene:
            nav_state = NavigationState(current_scene=self.schema.initial_scene)
            await self.navigation_manager.save_state(
                bot_id,
                event_data.user_id,
                event_data.chat_id or event_data.user_id,
                nav_state,
            )

        return nav_state

    async def _check_conditions(self, handler: Any, context: ExecutionContext) -> bool:
        """
        Проверить условия handler

        Args:
            handler: EventHandler из schema
            context: Контекст выполнения

        Returns:
            True если все условия выполнены
        """
        if not handler.conditions:
            return True

        for condition in handler.conditions:
            var_value = await self._get_variable_for_condition(condition.variable, context)

            compare_value = await self._resolve_condition_value(condition.value, context)

            if not ConditionEvaluator._apply_operator(var_value, condition.operator, compare_value):
                return False

        return True

    async def _get_variable_for_condition(self, var_name: str, context: ExecutionContext) -> Any:
        """Получить значение переменной для условия"""
        value = context.get_flag(var_name)
        if value is not None:
            return value

        bot_id = context.bot.id if context.bot else context.event_data.get("bot_id")
        user_id = context.user_id
        chat_id = context.chat_id

        for scope_name in ["user", "chat", "bot"]:
            try:
                result = await self.variable_repository.get(
                    bot_id=bot_id,
                    name=var_name,
                    scope=scope_name,
                    user_id=user_id if scope_name == "user" else None,
                    chat_id=chat_id if scope_name == "chat" else None,
                )
                if result is not None:
                    return result
            except Exception:
                continue

        return None

    async def _resolve_condition_value(self, value: Any, context: ExecutionContext) -> Any:
        """Разрешить значение условия (может быть ссылка на переменную)"""
        if isinstance(value, str) and value.startswith("{") and value.endswith("}"):
            var_name = value[1:-1]
            return await self._get_variable_for_condition(var_name, context)

        return value

    async def _execute_actions(self, handler: Any, context: ExecutionContext) -> None:
        """
        Выполнить actions handler

        Args:
            handler: EventHandler из schema
            context: Контекст выполнения
        """
        for action in handler.actions:
            try:
                await self.action_executor.execute(action, context, context.event)
            except Exception:
                logger.exception("Error executing action %s for event '%s'", action.type, handler.event_name)

    @staticmethod
    def _get_event_mode(source_type: EventSourceType) -> EventMode:
        """
        Определить режим события по source_type

        Args:
            source_type: Тип источника события

        Returns:
            EventMode
        """
        if source_type == EventSourceType.SCHEDULED:
            return EventMode.SCHEDULED
        if source_type == EventSourceType.WEBHOOK:
            return EventMode.WEBHOOK
        return EventMode.MESSAGE
