import logging
from typing import Any, Awaitable, Callable, Dict, Optional, Union

from aiogram import BaseMiddleware, Bot
from aiogram.exceptions import (
    AiogramError,
    TelegramAPIError,
    TelegramForbiddenError,
)
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, Message, Update, User
from sqlalchemy.ext.asyncio import AsyncSession

from config.settings import Settings
from db.dal import user_dal

from ..keyboards.inline.user_keyboards import get_user_banned_keyboard
from .i18n import JsonI18n


class BanCheckMiddleware(BaseMiddleware):
    def __init__(self, settings: Settings, i18n_instance: JsonI18n):
        super().__init__()
        self.settings = settings
        self.i18n_main_instance = i18n_instance

    async def __call__(
        self,
        handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
        event: Update,
        data: Dict[str, Any],
    ) -> Any:
        session: AsyncSession = data["session"]
        event_user: Optional[User] = data.get("event_from_user")
        bot_instance: Bot = data["bot"]

        if not event_user:
            return await handler(event, data)

        if event_user.id in self.settings.ADMIN_IDS:
            return await handler(event, data)

        try:
            db_user_model = await user_dal.get_user_by_id(session, event_user.id)
        except Exception as e_db:
            logging.error(
                f"BanCheckMiddleware: DB error fetching user {event_user.id}: {e_db}", exc_info=True
            )
            return await handler(event, data)

        if db_user_model and db_user_model.is_banned:
            logging.info(
                f"User {event_user.id} ({event_user.username or 'NoUsername'}) is banned. Blocking access."  # noqa: E501
            )

            i18n_data_from_event = data.get("i18n_data", {})
            current_lang = i18n_data_from_event.get(
                "current_language", self.settings.DEFAULT_LANGUAGE
            )
            i18n_to_use: Optional[JsonI18n] = i18n_data_from_event.get(
                "i18n_instance", self.i18n_main_instance
            )

            ban_message_text = "You are banned. Please contact support."
            keyboard: Optional[InlineKeyboardMarkup] = None

            if i18n_to_use:
                _ = lambda k, **kw: i18n_to_use.gettext(current_lang, k, **kw)
                ban_message_text = _("user_is_banned")
                keyboard = get_user_banned_keyboard(
                    self.settings.SUPPORT_LINK, current_lang, i18n_to_use
                )
            elif self.settings.SUPPORT_LINK:
                from aiogram.utils.keyboard import InlineKeyboardBuilder

                builder = InlineKeyboardBuilder()
                builder.button(text="Support", url=self.settings.SUPPORT_LINK)
                keyboard = builder.as_markup()

            actual_event_object: Optional[Union[Message, CallbackQuery]] = None
            if event.message:
                actual_event_object = event.message
            elif event.callback_query:
                actual_event_object = event.callback_query

            try:
                if isinstance(actual_event_object, Message):
                    await actual_event_object.answer(ban_message_text, reply_markup=keyboard)
                elif isinstance(actual_event_object, CallbackQuery):
                    await actual_event_object.answer(ban_message_text, show_alert=True)
                    if actual_event_object.message:
                        try:
                            await actual_event_object.message.edit_text(
                                ban_message_text, reply_markup=keyboard
                            )
                        except (TelegramAPIError, AiogramError):
                            await bot_instance.send_message(
                                actual_event_object.from_user.id,
                                ban_message_text,
                                reply_markup=keyboard,
                            )
                    else:
                        await bot_instance.send_message(
                            actual_event_object.from_user.id,
                            ban_message_text,
                            reply_markup=keyboard,
                        )
                else:
                    await bot_instance.send_message(
                        event_user.id, ban_message_text, reply_markup=keyboard
                    )
                logging.info(f"Ban notification sent to user {event_user.id}.")
            except TelegramForbiddenError:
                logging.warning(f"BanCheck: Bot is blocked by user {event_user.id}.")
            except Exception as e_send:
                logging.error(
                    f"BanCheck: Failed to notify banned user {event_user.id}: {type(e_send).__name__} - {e_send}",  # noqa: E501
                    exc_info=True,
                )

            return
        return await handler(event, data)
