"""Структурированное логирование через structlog."""

from __future__ import annotations

import time
from typing import Any, TYPE_CHECKING

from aiogram import BaseMiddleware, Router

if TYPE_CHECKING:
    from aiogram.types import TelegramObject
    from collections.abc import Awaitable, Callable


def configure_structlog(json_output: bool = False, level: str = "INFO") -> None:
    """Настроить structlog для приложения.

    Args:
        json_output: True → JSONRenderer (prod), False → ConsoleRenderer (dev)
        level: Уровень логирования (DEBUG, INFO, WARNING, ERROR)
    """
    try:
        import logging

        import structlog

        structlog.configure(
            processors=[
                structlog.contextvars.merge_contextvars,
                structlog.processors.add_log_level,
                structlog.processors.StackInfoRenderer(),
                structlog.dev.set_exc_info,
                structlog.processors.format_exc_info,
                structlog.processors.TimeStamper(fmt="iso"),
                structlog.processors.JSONRenderer() if json_output else structlog.dev.ConsoleRenderer(),
            ],
            wrapper_class=structlog.make_filtering_bound_logger(logging.getLevelName(level)),
            context_class=dict,
            logger_factory=structlog.PrintLoggerFactory(),
            cache_logger_on_first_use=True,
        )
    except ImportError:
        import logging

        logging.getLogger(__name__).warning("structlog not installed, structured logging disabled")


class StructlogMiddleware(BaseMiddleware):
    """Outer middleware — один структурированный лог на каждый обработанный запрос."""

    async def __call__(
        self,
        handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
        event: TelegramObject,
        data: dict[str, Any],
    ) -> Any:
        try:
            import structlog

            log = structlog.get_logger()
        except ImportError:
            return await handler(event, data)

        start_time = time.monotonic()
        error_info: str | None = None

        try:
            result = await handler(event, data)
        except Exception as exc:
            error_info = f"{type(exc).__name__}: {exc}"
            raise
        finally:
            duration_ms = round((time.monotonic() - start_time) * 1000, 2)

            bot_id = data.get("bot_id")
            user_id = data.get("user_id")
            chat_id = data.get("chat_id")

            context = data.get("context")
            scene_id = getattr(context, "scene_id", None) if context else None

            log_data: dict[str, Any] = {
                "bot_id": bot_id,
                "user_id": user_id,
                "chat_id": chat_id,
                "event_type": type(event).__name__,
                "duration_ms": duration_ms,
            }
            if scene_id:
                log_data["scene_id"] = scene_id
            if error_info:
                log_data["error"] = error_info
                log.error("request_failed", **log_data)
            else:
                log.info("request_processed", **log_data)

        return result

    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)
