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

from aiogram import BaseMiddleware
from aiogram.types import Update
from aiogram.types import User as TgUser
from sqlalchemy.ext.asyncio import AsyncSession

from bot.infra.redis import cache_get_json, cache_set_json, redis_key
from bot.utils.text_sanitizer import sanitize_display_name, sanitize_username
from config.settings import Settings
from db.dal import user_dal

_LOCAL_PROFILE_SYNC_CHECKS: Dict[int, float] = {}


class ProfileSyncMiddleware(BaseMiddleware):
    async def __call__(
        self,
        handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
        event: Update,
        data: Dict[str, Any],
    ) -> Any:
        session: AsyncSession = data.get("session")
        tg_user: Optional[TgUser] = data.get("event_from_user")
        settings: Optional[Settings] = data.get("settings")

        if session and tg_user:
            if settings and await _profile_sync_recently_checked(settings, int(tg_user.id)):
                return await handler(event, data)

            try:
                db_user = await user_dal.get_user_by_telegram_id(session, tg_user.id)
                if not db_user:
                    db_user = await user_dal.get_user_by_id(session, tg_user.id)
                if db_user:
                    update_payload: Dict[str, Any] = {}
                    sanitized_username = sanitize_username(tg_user.username)
                    sanitized_first_name = sanitize_display_name(tg_user.first_name)
                    sanitized_last_name = sanitize_display_name(tg_user.last_name)

                    if db_user.telegram_id != tg_user.id:
                        update_payload["telegram_id"] = tg_user.id
                    if db_user.username != sanitized_username:
                        update_payload["username"] = sanitized_username
                    if db_user.first_name != sanitized_first_name:
                        update_payload["first_name"] = sanitized_first_name
                    if db_user.last_name != sanitized_last_name:
                        update_payload["last_name"] = sanitized_last_name

                    if update_payload:
                        await user_dal.update_user(session, db_user.user_id, update_payload)
                        logging.info(
                            f"ProfileSyncMiddleware: Updated user {tg_user.id} profile fields: {list(update_payload.keys())}"  # noqa: E501
                        )

                        # Keep panel identity fields fresh, but do not rewrite
                        # description from profile changes. Remnawave may return
                        # description with lossy encoding in list views.
                        try:
                            panel_service = data.get("panel_service")
                            if panel_service and db_user.panel_user_uuid:
                                panel_payload = {
                                    "telegramId": tg_user.id,
                                }
                                if db_user.email:
                                    panel_payload["email"] = db_user.email
                                await panel_service.update_user_details_on_panel(
                                    db_user.panel_user_uuid,
                                    panel_payload,
                                )
                        except Exception as e_upd_desc:
                            logging.warning(
                                f"ProfileSyncMiddleware: Failed to update panel identity for user {tg_user.id}: {e_upd_desc}"  # noqa: E501
                            )
            except Exception as e:
                logging.error(
                    f"ProfileSyncMiddleware: Failed to sync profile for user {getattr(tg_user, 'id', 'N/A')}: {e}",  # noqa: E501
                    exc_info=True,
                )
            finally:
                if settings:
                    await _mark_profile_sync_checked(settings, int(tg_user.id))

        return await handler(event, data)


async def _profile_sync_recently_checked(settings: Settings, telegram_id: int) -> bool:
    ttl_seconds = int(getattr(settings, "PROFILE_SYNC_CACHE_TTL_SECONDS", 900) or 0)
    if ttl_seconds <= 0:
        return False

    now = time.monotonic()
    expires_at = _LOCAL_PROFILE_SYNC_CHECKS.get(telegram_id)
    if expires_at and expires_at > now:
        return True

    key = redis_key(settings, "cache", "profile-sync", telegram_id)
    try:
        cached = await cache_get_json(settings, key)
    except Exception:
        cached = None
    if cached:
        _LOCAL_PROFILE_SYNC_CHECKS[telegram_id] = now + ttl_seconds
        return True
    return False


async def _mark_profile_sync_checked(settings: Settings, telegram_id: int) -> None:
    ttl_seconds = int(getattr(settings, "PROFILE_SYNC_CACHE_TTL_SECONDS", 900) or 0)
    if ttl_seconds <= 0:
        return

    _LOCAL_PROFILE_SYNC_CHECKS[telegram_id] = time.monotonic() + ttl_seconds
    key = redis_key(settings, "cache", "profile-sync", telegram_id)
    try:
        await cache_set_json(settings, key, {"checked": True}, ttl_seconds)
    except Exception:
        pass
