"""
Сервис для обработки handlers

Отвечает за выполнение handlers разных типов:
- Fallback handlers
- Global handlers
- Scene handlers (с навигацией и переходами)
"""

import logging
from typing import TYPE_CHECKING

from typing import Any as AnyEvent

from aiogram.types import CallbackQuery, Message

from ui_router.analytics import AnalyticsEvent, AnalyticsEventType
from ui_router.schema import TransitionType

if TYPE_CHECKING:
    from ui_router.analytics import AnalyticsCollector
    from ui_router.state.context import ExecutionContext
    from ui_router.rules.flags import FlagResolver
    from ui_router.execution.actions import ActionExecutor
    from ui_router.schema import GlobalHandler, Handler, Scene, UIRouter
    from ui_router.services.navigation_service import NavigationService
    from ui_router.services.scene_renderer import SceneRenderer
    from ui_router.execution.throttle import ThrottleChecker

logger = logging.getLogger(__name__)


class HandlerService:
    """Сервис для обработки handlers"""

    def __init__(
        self,
        action_executor: "ActionExecutor",
        navigation_service: "NavigationService",
        scene_renderer: "SceneRenderer",
        flag_resolver: "FlagResolver",
        schema: "UIRouter",
        analytics_collector: "AnalyticsCollector | None" = None,
        throttle_checker: "ThrottleChecker | None" = None,
    ) -> None:
        self.action_executor = action_executor
        self.navigation_service = navigation_service
        self.scene_renderer = scene_renderer
        self.flag_resolver = flag_resolver
        self.schema = schema
        self.analytics_collector = analytics_collector
        self.throttle_checker = throttle_checker

    async def _track(
        self, event_type: AnalyticsEventType, context: "ExecutionContext", **kwargs: object
    ) -> None:
        """Отправить analytics event если коллектор настроен."""
        if self.analytics_collector:
            await self.analytics_collector.track(
                AnalyticsEvent(
                    event_type=event_type,
                    bot_id=context.bot.id,
                    user_id=context.user_id,
                    chat_id=context.chat_id,
                    **kwargs,
                )
            )

    async def process_handler(
        self, handler: "Handler", event: Message | CallbackQuery, context: "ExecutionContext"
    ) -> None:
        """
        Обработать handler (для fallback и других случаев).

        Args:
            handler: Handler для обработки
            event: Событие (Message или CallbackQuery)
            context: ExecutionContext
        """
        if handler.throttle and self.throttle_checker:
            allowed = await self.throttle_checker.check(
                handler.name, handler.throttle, context.user_id, context.chat_id,
            )
            if not allowed:
                if handler.throttle.cooldown_action:
                    await self.action_executor.execute(handler.throttle.cooldown_action, context, event)
                return

        for action in handler.actions:
            try:
                await self.action_executor.execute(action, context, event)
            except Exception:
                logger.exception("Error executing action %s", action.type)

    async def process_global_handler(
        self, schema_handler: "GlobalHandler", event: AnyEvent, context: "ExecutionContext"
    ) -> None:
        """
        Обработать глобальный хендлер.

        Args:
            schema_handler: GlobalHandler для обработки
            event: Событие (Message, CallbackQuery, ChatMemberUpdated, ChatJoinRequest, etc.)
            context: ExecutionContext
        """
        if schema_handler.throttle and self.throttle_checker:
            allowed = await self.throttle_checker.check(
                schema_handler.name, schema_handler.throttle, context.user_id, context.chat_id,
            )
            if not allowed:
                if schema_handler.throttle.cooldown_action:
                    await self.action_executor.execute(schema_handler.throttle.cooldown_action, context, event)
                return

        for action in schema_handler.actions:
            await self.action_executor.execute(action, context, event)

        await self._track(
            AnalyticsEventType.HANDLER_EXECUTED, context,
            scene_id=context.scene_id, handler_name=schema_handler.name,
        )

        try:
            await context.save_to_storage()
        except Exception:
            logger.exception("Failed to save navigation state after global handler")

        pending_back = context.get_flag("_pending_back")
        pending_scene = context.get_flag("_pending_scene")
        previous_scene: str | None = None

        if pending_back:
            back_state, previous_scene, history_transition = await self.navigation_service.go_back(
                context.bot.id, context.user_id, context.chat_id
            )
            if previous_scene:
                pending_scene = previous_scene
                context.navigation_state = back_state
                if not context.get_flag("_pending_transition"):
                    context.set_flag("_pending_transition", history_transition)
            elif self.schema.initial_scene:
                pending_scene = self.schema.initial_scene

        if pending_scene:
            await self._track(
                AnalyticsEventType.NAVIGATION, context,
                scene_id=pending_scene,
                metadata={"from_scene": context.scene_id, "type": "back" if pending_back else "goto"},
            )

            current_scene = self.schema.get_scene(context.scene_id)
            if current_scene and current_scene.on_exit:
                for action in current_scene.on_exit:
                    await self.action_executor.execute(action, context, event)

            if not pending_back or not previous_scene:
                pending_transition = context.get_flag("_pending_transition", "send")
                new_state = await self.navigation_service.navigate_to(
                    context.bot.id,
                    context.user_id,
                    context.chat_id,
                    pending_scene,
                    save_to_history=not pending_back,
                    transition_type=TransitionType(pending_transition),
                )
                context.navigation_state = new_state

            pending_transition = context.get_flag("_pending_transition", "send")

            await self.scene_renderer.enter_scene(
                pending_scene,
                context,
                message=event if isinstance(event, Message) else None,
                transition=TransitionType(pending_transition),
                callback_query=event if isinstance(event, CallbackQuery) else None,
            )

        if schema_handler.interrupt_propagation and isinstance(event, CallbackQuery):
            await event.answer()

    async def process_scene_handler(
        self, scene: "Scene", schema_handler: "Handler", event: Message | CallbackQuery, context: "ExecutionContext"
    ) -> None:
        """
        Обработать хендлер сцены.

        Выполняет:
        1. Установку scene_id в контексте
        2. Резолв флагов сцены
        3. Выполнение actions
        4. Сохранение контекста
        5. Обработку pending переходов (context flags: _pending_scene, _pending_back)
        6. Выполнение on_exit текущей сцены при переходе
        7. Навигацию к новой сцене

        Args:
            scene: Сцена в которой выполняется handler
            schema_handler: Handler для обработки
            event: Событие (Message или CallbackQuery)
            context: ExecutionContext
        """
        context.scene_id = scene.id

        await self.flag_resolver.resolve_scene_flags(scene, context, event)

        if schema_handler.throttle and self.throttle_checker:
            allowed = await self.throttle_checker.check(
                schema_handler.name, schema_handler.throttle, context.user_id, context.chat_id,
            )
            if not allowed:
                if schema_handler.throttle.cooldown_action:
                    await self.action_executor.execute(schema_handler.throttle.cooldown_action, context, event)
                return

        for action in schema_handler.actions:
            await self.action_executor.execute(action, context, event)

        await self._track(
            AnalyticsEventType.HANDLER_EXECUTED, context,
            scene_id=scene.id, handler_name=schema_handler.name,
        )

        try:
            await context.save_to_storage()
        except Exception:
            logger.exception("Failed to save navigation state after scene handler")

        pending_back = context.get_flag("_pending_back")
        pending_scene = context.get_flag("_pending_scene")
        previous_scene: str | None = None

        if pending_back:
            back_state, previous_scene, history_transition = await self.navigation_service.go_back(
                context.bot.id, context.user_id, context.chat_id
            )
            if previous_scene:
                pending_scene = previous_scene
                context.navigation_state = back_state
                if not context.get_flag("_pending_transition"):
                    context.set_flag("_pending_transition", history_transition)
            elif self.schema.initial_scene:
                pending_scene = self.schema.initial_scene

        if pending_scene:
            await self._track(
                AnalyticsEventType.NAVIGATION, context,
                scene_id=pending_scene,
                metadata={"from_scene": context.scene_id, "type": "back" if pending_back else "goto"},
            )

            current_scene = self.schema.get_scene(context.scene_id)
            if current_scene and current_scene.on_exit:
                for action in current_scene.on_exit:
                    await self.action_executor.execute(action, context, event)

            if not pending_back or not previous_scene:
                pending_transition = context.get_flag("_pending_transition", "send")
                new_state = await self.navigation_service.navigate_to(
                    context.bot.id,
                    context.user_id,
                    context.chat_id,
                    pending_scene,
                    save_to_history=not pending_back,
                    transition_type=TransitionType(pending_transition),
                )
                context.navigation_state = new_state

            pending_transition = context.get_flag("_pending_transition", "send")

            await self.scene_renderer.enter_scene(
                pending_scene,
                context,
                message=event if isinstance(event, Message) else None,
                transition=TransitionType(pending_transition),
                callback_query=event if isinstance(event, CallbackQuery) else None,
            )
