from typing import Callable, Awaitable, Any, Dict

from aiogram import Bot, BaseMiddleware
from aiogram.types import TelegramObject, Message, CallbackQuery, Update

from middleware.loggers import logger


class ErrorHandlingMiddleware(BaseMiddleware):
    """
    Middleware для глобальной обработки ошибок в хендлерах.

    Зачем нужен:
    - Централизованная обработка исключений
    - Уведомление администраторов об ошибках
    - Graceful degradation при сбоях
    """

    def __init__(self, admin_ids: list[int]):
        """
        Инициализация middleware обработки ошибок.

        Args:
            admin_ids: Список ID администраторов для уведомлений
        """
        self.admin_ids = admin_ids
        super().__init__()

    async def __call__(
            self,
            handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
            event: TelegramObject,
            data: Dict[str, Any]
    ) -> Any:
        """
        Перехватывает и обрабатывает ошибки в хендлерах.
        """
        try:
            return await handler(event, data)

        except Exception as e:
            # Получаем информацию о пользователе безопасным способом
            user_str: str = self._extract_user_info(event)

            # Логируем ошибку
            error_message: str = f"Ошибка в хендлере: {type(e).__name__}: {str(e)}"

            logger.error(
                text=error_message,
                log_type="HANDLER_ERROR",
                user=user_str
            )

            # Уведомляем администраторов
            await self._notify_admins(error_message, event, user_str)

            # Отправляем пользователю сообщение об ошибке
            await self._send_error_message(event, user_str)

            return None

    @staticmethod
    def _extract_user_info(event: TelegramObject) -> str:
        """
        Безопасно извлекает информацию о пользователе из события.

        Args:
            event: Объект события

        Returns:
            Строка с идентификатором пользователя
        """
        user_str: str = "@System"

        # Для Message и CallbackQuery
        if isinstance(event, (Message, CallbackQuery)) and hasattr(event, 'from_user') and event.from_user:
            user = event.from_user
            user_str = f"@{user.username}" if user.username else f"id{user.id}"

        # Для Update (который содержит message или callback_query)
        elif isinstance(event, Update):
            # Пытаемся найти пользователя в различных полях Update
            user_object = None
            if event.message and event.message.from_user:
                user_object = event.message.from_user
            elif event.edited_message and event.edited_message.from_user:
                user_object = event.edited_message.from_user
            elif event.callback_query and event.callback_query.from_user:
                user_object = event.callback_query.from_user
            elif event.channel_post and event.channel_post.from_user:
                user_object = event.channel_post.from_user
            elif event.edited_channel_post and event.edited_channel_post.from_user:
                user_object = event.edited_channel_post.from_user

            if user_object:
                user_str = f"@{user_object.username}" if user_object.username else f"id{user_object.id}"

        return user_str

    @staticmethod
    def _extract_event_text(event: TelegramObject) -> str:
        """
        Безопасно извлекает текст из события.

        Args:
            event: Объект события

        Returns:
            Текст события или пустая строка
        """
        event_text: str = ""

        # Для Message
        if isinstance(event, Message) and hasattr(event, 'text') and event.text:
            event_text: str = event.text
        # Для CallbackQuery
        elif isinstance(event, CallbackQuery) and hasattr(event, 'data') and event.data:
            event_text: str = f"callback: {event.data}"
        # Для Update
        elif isinstance(event, Update):
            if event.message and event.message.text:
                event_text: str = event.message.text
            elif event.callback_query and event.callback_query.data:
                event_text: str = f"callback: {event.callback_query.data}"
            elif event.edited_message and event.edited_message.text:
                event_text: str = event.edited_message.text

        return event_text[:100] + "..." if len(event_text) > 100 else event_text

    async def _notify_admins(
            self,
            error_message: str,
            event: TelegramObject,
            user_str: str
    ) -> None:
        """Уведомляет администраторов об ошибке."""
        bot: Bot = event.bot if hasattr(event, 'bot') else None

        if bot:
            for admin_id in self.admin_ids:
                try:
                    event_info: str = f"Событие: {type(event).__name__}"
                    event_text: str = self._extract_event_text(event)
                    if event_text:
                        event_info += f", текст: {event_text}"

                    full_message: str = (
                        f"🚨 Ошибка в боте:\n\n"
                        f"Пользователь: {user_str}\n"
                        f"Ошибка: {error_message}\n"
                        f"{event_info}"
                    )

                    await bot.send_message(admin_id, full_message)

                    logger.info(
                        text=f"Администратор {admin_id} уведомлен об ошибке",
                        log_type="ADMIN_NOTIFIED",
                        user=user_str
                    )

                except Exception as e:
                    logger.error(
                        text=f"Не удалось уведомить админа {admin_id}: {e}",
                        log_type="ADMIN_NOTIFY_ERROR",
                        user=user_str
                    )

    @staticmethod
    async def _send_error_message(
            event: TelegramObject,
            user_str: str
    ) -> None:
        """Отправляет пользователю сообщение об ошибке."""
        error_text: str = (
            "⚠️ Произошла непредвиденная ошибка. "
            "Разработчики уже уведомлены и работают над исправлением.\n\n"
            "Попробуйте повторить действие позже или нажмите /start"
        )

        try:
            if isinstance(event, Message):
                await event.answer(error_text)
            elif isinstance(event, CallbackQuery):
                await event.message.answer(error_text)
                await event.answer()
            elif isinstance(event, Update) and event.message:
                await event.message.answer(error_text)

            logger.info(
                text="Пользователю отправлено сообщение об ошибке",
                log_type="ERROR_MESSAGE_SENT",
                user=user_str
            )

        except Exception as e:
            logger.error(
                text=f"Не удалось отправить сообщение об ошибке: {e}",
                log_type="ERROR_MESSAGE_FAILED",
                user=user_str
            )
