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

from aiogram import Router, BaseMiddleware
from aiogram.types import TelegramObject, CallbackQuery

from ui_router.execution.callbacks import CallbackDataManager, CallbackManagerOuterMiddleware, UICallbackData
from ui_router.state.context import NavigationManager, NavigationState, ExecutionContext


class UIDataMiddleware(BaseMiddleware):
    async def __call__(
        self,
        handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
        event: TelegramObject,
        data: dict[str, Any],
    ) -> Any:
        event_from_user = data.get("event_from_user")
        event_chat = data.get("event_chat")
        bot = data.get("bot")

        if event_from_user:
            data["user_id"] = event_from_user.id
        else:
            data["user_id"] = None

        if event_chat:
            data["chat_id"] = event_chat.id
        else:
            data["chat_id"] = None

        if bot:
            data["bot_id"] = bot.id
        else:
            data["bot_id"] = None

        return await handler(event, data)

    def setup(self, router: Router) -> None:
        router.message.outer_middleware(self)
        router.callback_query.outer_middleware(self)
        router.edited_message.outer_middleware(self)
        router.my_chat_member.outer_middleware(self)
        router.chat_member.outer_middleware(self)
        router.chat_boost.outer_middleware(self)
        router.removed_chat_boost.outer_middleware(self)
        router.chat_join_request.outer_middleware(self)
        router.pre_checkout_query.outer_middleware(self)
        router.inline_query.outer_middleware(self)
        router.chosen_inline_result.outer_middleware(self)


class UIContextMiddleware(BaseMiddleware):
    def __init__(self, navigation_manager: NavigationManager) -> None:
        self.navigation_manager = navigation_manager

    async def __call__(
        self,
        handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
        event: TelegramObject,
        data: dict[str, Any],
    ) -> Any:
        user_id = data.get("user_id")
        chat_id = data.get("chat_id")
        bot_id = data.get("bot_id")
        bot = data.get("bot")

        if user_id is not None:
            nav_state = await self.navigation_manager.get_state(bot_id, user_id, chat_id)
        else:
            nav_state = NavigationState(current_scene="")

        data["nav_state"] = nav_state

        context = ExecutionContext(
            navigation_manager=self.navigation_manager,
            navigation_state=nav_state,
            bot=bot,
            scene_id=nav_state.current_scene,
            user_id=user_id,
            chat_id=chat_id,
            event_data={k: v for k, v in data.items() if k != "handler"},
        )
        if isinstance(event, CallbackQuery):
            ui_call_data: UICallbackData | None = data.get("ui_call_data")
            if ui_call_data and ui_call_data.params:
                for key, value in ui_call_data.params.items():
                    context.save_user_input(key, value)
                await context.save_to_storage()

        data["context"] = context

        return await handler(event, data)

    def setup(self, router: Router) -> None:
        router.message.outer_middleware(self)
        router.callback_query.outer_middleware(self)
        router.edited_message.outer_middleware(self)
        router.my_chat_member.outer_middleware(self)
        router.chat_member.outer_middleware(self)
        router.chat_boost.outer_middleware(self)
        router.removed_chat_boost.outer_middleware(self)
        router.chat_join_request.outer_middleware(self)
        router.pre_checkout_query.outer_middleware(self)
        router.inline_query.outer_middleware(self)
        router.chosen_inline_result.outer_middleware(self)


def install_middleware(
    router: Router, callback_manager: CallbackDataManager, navigation_manager: NavigationManager,
    enable_structlog: bool = False,
) -> None:
    if enable_structlog:
        from ui_router.logging import StructlogMiddleware

        StructlogMiddleware().setup(router)

    UIDataMiddleware().setup(router)

    router.callback_query.outer_middleware(CallbackManagerOuterMiddleware(callback_manager))

    UIContextMiddleware(navigation_manager).setup(router)
