import asyncio

from datetime import datetime

from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession

from config import REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, SUPERNODE
from database import delete_notification, get_servers
from database.models import Key, Server, Tariff
from database.notifications import clear_hot_lead_notifications
from logger import logger
from panels._3xui import ClientConfig, add_client, extend_client_key, get_xui_instance
from panels.remnawave import RemnawaveAPI


async def renew_key_in_cluster(
    cluster_id: str,
    email: str,
    client_id: str,
    new_expiry_time: int,
    total_gb: int,
    session: AsyncSession,
    hwid_device_limit: int = 0,
    reset_traffic: bool = True,
):
    try:
        servers = await get_servers(session)
        cluster = servers.get(cluster_id)

        if not cluster:
            found_servers = []
            for _key, server_list in servers.items():
                for server_info in server_list:
                    if server_info.get("server_name", "").lower() == cluster_id.lower():
                        found_servers.append(server_info)
            if found_servers:
                cluster = found_servers
            else:
                raise ValueError(f"Кластер или сервер с ID/именем {cluster_id} не найден.")

        result = await session.execute(select(Key.tg_id, Key.server_id).where(Key.client_id == client_id).limit(1))
        row = result.first()
        if not row:
            logger.error(f"Не найден пользователь с client_id={client_id} в таблице keys.")
            return False

        tg_id, server_id = row

        result = await session.execute(select(Server.tariff_group).where(Server.server_name == server_id))
        tariff_group_row = result.scalar_one_or_none()

        if tariff_group_row:
            result = await session.execute(
                select(Tariff)
                .where(Tariff.group_code == tariff_group_row, Tariff.is_active.is_(True))
                .order_by(Tariff.duration_days.desc())
                .limit(1)
            )
            tariff = result.scalar_one_or_none()
            if tariff and tariff.device_limit is not None:
                hwid_device_limit = int(tariff.device_limit)

        remnawave_inbound_ids = []
        tasks = []
        for server_info in cluster:
            if server_info.get("panel_type", "3x-ui").lower() == "remnawave":
                inbound_id = server_info.get("inbound_id")
                if inbound_id:
                    remnawave_inbound_ids.append(inbound_id)

        if remnawave_inbound_ids:
            remnawave_server = next(
                (
                    s
                    for s in cluster
                    if s.get("panel_type", "").lower() == "remnawave" and s.get("inbound_id") in remnawave_inbound_ids
                ),
                None,
            )
            if remnawave_server:
                remna = RemnawaveAPI(remnawave_server["api_url"])
                if await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD):
                    expire_iso = datetime.utcfromtimestamp(new_expiry_time // 1000).isoformat() + "Z"
                    traffic_limit_bytes = total_gb * 1024 * 1024 * 1024 if total_gb else 0
                    updated = await remna.update_user(
                        uuid=client_id,
                        expire_at=expire_iso,
                        active_user_inbounds=remnawave_inbound_ids,
                        traffic_limit_bytes=traffic_limit_bytes,
                        hwid_device_limit=hwid_device_limit,
                    )
                    if updated:
                        logger.info(f"Подписка Remnawave {client_id} успешно продлена")
                        if reset_traffic:
                            await remna.reset_user_traffic(client_id)
                    else:
                        logger.warning(f"Не удалось продлить подписку Remnawave {client_id}, пробуем создать")
                        result = await session.execute(
                            select(Key.remnawave_link, Key.key).where(Key.client_id == client_id)
                        )
                        row = result.one_or_none()
                        remnawave_link = row[0] if row else None
                        row[1] if row else None

                        user_data = {
                            "username": email,
                            "trafficLimitStrategy": "NO_RESET",
                            "expireAt": expire_iso,
                            "telegramId": tg_id,
                            "activeInternalSquads": remnawave_inbound_ids,
                        }
                        if remnawave_link and "/" in remnawave_link:
                            user_data["shortUuid"] = remnawave_link.rstrip("/").split("/")[-1]
                        if traffic_limit_bytes and traffic_limit_bytes > 0:
                            user_data["trafficLimitBytes"] = traffic_limit_bytes
                        if hwid_device_limit is not None:
                            user_data["hwidDeviceLimit"] = hwid_device_limit

                        result = await remna.create_user(user_data)
                        if result:
                            new_client_id = result.get("uuid")
                            new_remnawave_link = result.get("subscriptionUrl")
                            logger.info(f"Пользователь Remnawave {client_id} успешно создан")

                            await session.execute(
                                update(Key)
                                .where(Key.client_id == client_id)
                                .values(client_id=new_client_id, remnawave_link=new_remnawave_link)
                            )
                            await session.commit()
                        else:
                            logger.error(f"Не удалось создать пользователя Remnawave {client_id}")
                else:
                    logger.error("Не удалось войти в Remnawave API")

        tasks = []

        for server_info in cluster:
            if server_info.get("panel_type", "3x-ui").lower() != "3x-ui":
                continue

            inbound_id = server_info.get("inbound_id")
            server_name = server_info.get("server_name", "unknown")

            if not inbound_id:
                logger.warning(f"INBOUND_ID отсутствует для сервера {server_name}. Пропуск.")
                continue

            if SUPERNODE:
                unique_email = f"{email}_{server_name.lower()}"
                sub_id = email
            else:
                unique_email = email
                sub_id = unique_email

            traffic_bytes = total_gb * 1024 * 1024 * 1024 if total_gb else 0

            async def process_server(server_info, inbound_id, unique_email, sub_id, server_name):
                try:
                    xui = await get_xui_instance(server_info["api_url"])
                except Exception as e:
                    logger.warning(f"[{server_name}] недоступна панель 3x-ui: {e}")
                    return server_name, False, f"api_unavailable: {e}"

                try:
                    updated = await extend_client_key(
                        xui=xui,
                        inbound_id=int(inbound_id),
                        email=unique_email,
                        new_expiry_time=new_expiry_time,
                        client_id=client_id,
                        total_gb=traffic_bytes,
                        sub_id=sub_id,
                        tg_id=tg_id,
                        limit_ip=hwid_device_limit,
                    )
                except Exception as e:
                    logger.warning(f"[{server_name}] ошибка при продлении: {e}")
                    updated = False

                if updated:
                    return server_name, True, None

                logger.warning(f"[{server_name}] не удалось обновить {unique_email}, пробуем создать")
                try:
                    config = ClientConfig(
                        client_id=client_id,
                        email=unique_email,
                        tg_id=tg_id,
                        limit_ip=hwid_device_limit if hwid_device_limit is not None else 0,
                        total_gb=traffic_bytes,
                        expiry_time=new_expiry_time,
                        enable=True,
                        flow="xtls-rprx-vision",
                        inbound_id=int(inbound_id),
                        sub_id=sub_id,
                    )
                    await add_client(xui, config)
                    return server_name, True, None
                except Exception as e:
                    logger.warning(f"[{server_name}] не удалось создать клиента: {e}")
                    return server_name, False, f"create_failed: {e}"

            tasks.append(process_server(server_info, inbound_id, unique_email, sub_id, server_name))

        results = await asyncio.gather(*tasks, return_exceptions=True)
        failed = []
        succeeded = []
        for r in results:
            if isinstance(r, Exception):
                failed.append(("unknown", f"task_exception: {r}"))
                continue
            name, ok, err = r
            if ok:
                succeeded.append(name)
            else:
                failed.append((name, err or "unknown_error"))
        if succeeded:
            logger.info(f"3x-ui продлено на: {', '.join(succeeded)}")
        if failed:
            logger.warning("3x-ui не продлено на: " + ", ".join([f"{n} ({e})" for n, e in failed]))

        await asyncio.gather(*tasks, return_exceptions=True)

        notification_prefixes = ["key_24h", "key_10h", "key_expired", "renew"]
        for notif in notification_prefixes:
            notification_id = f"{email}_{notif}"
            await delete_notification(session, tg_id, notification_id)
        logger.info(f"🧹 Уведомления для ключа {email} очищены при продлении.")

        try:
            await clear_hot_lead_notifications(session, tg_id)
        except Exception as e:
            logger.warning(f"Не удалось очистить уведомления о скидках для {tg_id} при продлении: {e}")

    except Exception as e:
        logger.error(f"Не удалось продлить ключ {client_id} в кластере/на сервере {cluster_id}: {e}")
        raise
