import logging
from datetime import datetime, timezone
from typing import Any, Awaitable, Callable, Dict, Optional

from aiogram import BaseMiddleware
from aiogram.types import CallbackQuery, Message, Update, User
from sqlalchemy.ext.asyncio import AsyncSession

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


class ActionLoggerMiddleware(BaseMiddleware):
    def __init__(self, settings: Settings):
        super().__init__()
        self.settings = settings

    async def __call__(
        self,
        handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
        event: Update,
        data: Dict[str, Any],
    ) -> Any:

        result = await handler(event, data)

        session: AsyncSession = data["session"]
        event_user: Optional[User] = data.get("event_from_user")

        user_id: Optional[int] = None
        telegram_username: Optional[str] = None
        telegram_first_name: Optional[str] = None
        content: Optional[str] = None
        is_admin_event_flag: bool = False
        target_user_id_for_log: Optional[int] = None

        if event_user:
            user_id = event_user.id
            telegram_username = event_user.username
            telegram_first_name = event_user.first_name
            if user_id in self.settings.ADMIN_IDS:
                is_admin_event_flag = True

        if is_admin_event_flag and not self.settings.LOG_ADMIN_ACTIONS:
            return result

        raw_update_snippet = None
        try:
            raw_update_snippet = event.model_dump_json(exclude_none=True, indent=None)[:1000]
        except AttributeError:
            raw_update_snippet = str(event)[:1000]
        except Exception:
            raw_update_snippet = str(event)[:1000]

        current_event_type = event.event_type

        if event.message:
            msg: Message = event.message
            if msg.text:
                content = msg.text
                if msg.text.startswith("/"):
                    current_event_type = f"command:{msg.text.split()[0]}"

            else:
                content = f"[{msg.content_type or 'unknown_content_type'}]"
                current_event_type = f"message:{msg.content_type or 'unknown'}"
        elif event.callback_query:
            cb: CallbackQuery = event.callback_query
            content = cb.data
            action_part = cb.data.split(":")[0] if cb.data and ":" in cb.data else cb.data
            current_event_type = f"callback:{action_part}"

        if user_id or current_event_type not in ["update"]:
            log_user_id_for_db = user_id
            if user_id:
                user_exists = await user_dal.get_user_by_id(session, user_id)
                if not user_exists:
                    logging.warning(
                        f"ActionLoggerMiddleware: User {user_id} not found in DB. Logging action with user_id=NULL."  # noqa: E501
                    )
                    log_user_id_for_db = None

            log_payload = {
                "user_id": log_user_id_for_db,
                "telegram_username": telegram_username,
                "telegram_first_name": telegram_first_name,
                "event_type": current_event_type,
                "content": content[:1000] if content else "N/A",
                "raw_update_preview": raw_update_snippet,
                "is_admin_event": is_admin_event_flag,
                "target_user_id": target_user_id_for_log,
                "timestamp": datetime.now(timezone.utc),
            }
            try:
                await message_log_dal.create_message_log_no_commit(session, log_payload)
            except Exception as e_log:
                logging.error(
                    f"ActionLoggerMiddleware: Failed to add log to session for user {user_id}, type {current_event_type}: {e_log}",  # noqa: E501
                    exc_info=True,
                )

        return result
