"""
Коннектор для работы с Amnezio Panel API через пакет `amnezio`.

Цель: дать интерфейс, похожий по стилю на `connectors/remnawave_conn.py`,
чтобы дальше заменить WireGuard-часть, которая сейчас завязана на 3x-ui.
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import date, datetime, time
from typing import Any, Optional, Union

from bot.utils import logger
from config import config


@dataclass(frozen=True)
class AmnezioKeypair:
    private_key: str
    preshared_key: str | None = None


class AmnezioConnector:
    """Обертка над `amnezio.AmnezioClient` с удобными методами."""

    def __init__(
        self,
        base_url: str | None = None,
        api_token: str | None = None,
        username: str | None = None,
        password: str | None = None,
        profile_id: int | None = None,
    ):
        self.base_url = (base_url if base_url is not None else config.AMNEZIO_BASE_URL).strip()
        self.api_token = (api_token if api_token is not None else config.AMNEZIO_API_TOKEN).strip()
        self.username = (username if username is not None else config.AMNEZIO_USERNAME).strip()
        self.password = (password if password is not None else config.AMNEZIO_PASSWORD).strip()
        self.profile_id = int(profile_id if profile_id is not None else config.AMNEZIO_PROFILE_ID)

        if not self.base_url:
            raise ValueError("AMNEZIO_BASE_URL is empty (set it in .env)")
        if not self.api_token and not (self.username and self.password):
            raise ValueError("Amnezio auth missing: set AMNEZIO_API_TOKEN or AMNEZIO_USERNAME/AMNEZIO_PASSWORD")

    def _client(self):
        # Импортируем лениво, чтобы не падать при старте, если пакет ещё не установлен.
        from amnezio import AmnezioClient  # type: ignore

        kwargs: dict[str, Any] = {"base_url": self.base_url}
        if self.api_token:
            kwargs["api_token"] = self.api_token
        else:
            kwargs["username"] = self.username
            kwargs["password"] = self.password

        return AmnezioClient(**kwargs)

    # =========================================================================
    # Базовые операции
    # =========================================================================

    async def generate_keypair(self) -> AmnezioKeypair:
        """Генерирует ключи WireGuard на стороне панели."""
        async with self._client() as client:
            kp = await client.users.generate_keypair()
            private_key = getattr(kp, "private_key", None) or kp.get("private_key")  # type: ignore[attr-defined]
            preshared_key = getattr(kp, "preshared_key", None) or kp.get("preshared_key")  # type: ignore[attr-defined]
            return AmnezioKeypair(private_key=private_key, preshared_key=preshared_key)

    async def list_users(self) -> Any:
        """Возвращает пагинированный список пользователей (объект из SDK)."""
        async with self._client() as client:
            return await client.users.list()

    async def get_user(self, user_id: int) -> Any | None:
        try:
            async with self._client() as client:
                return await client.users.get(user_id)
        except Exception as e:
            logger.error(f"Error getting Amnezio user: user_id={user_id}, error={e}")
            return None

    async def get_user_by_subid(self, subid: int) -> Any | None:
        """
        Получает пользователя по sub_id.
        """
        sub_id = str(subid)
        try:
            async with self._client() as client:
                return await client.users.get_by_sub_id(sub_id)
        except Exception as e:
            logger.error(f"Error getting Amnezio user by subid: subid={subid}, error={e}")
            return None

    async def create_user(
        self,
        subid: int,
        paid_for: Union[date, datetime],
        name: str | None = None,
        profile_id: int | None = None,
        keypair: Optional[AmnezioKeypair] = None,
    ) -> Any | None:
        """
        Создает пользователя (peer) в Amnezio.

        Args:
            subid: ID подписки (пишем в sub_id панели)
            paid_for: дата/время окончания подписки
            name: опциональное имя/описание (если API поддерживает)
            profile_id: профиль в панели
            keypair: ключи (если не переданы — генерим через panel)
        """
        try:
            if keypair is None:
                keypair = await self.generate_keypair()

            pid = int(profile_id if profile_id is not None else self.profile_id)
            sub_id = str(subid)
            # Нормализуем в datetime для SDK/БД
            if isinstance(paid_for, date) and not isinstance(paid_for, datetime):
                paid_for_dt = datetime.combine(paid_for, time.max)
            else:
                paid_for_dt = paid_for  # type: ignore[assignment]

            async with self._client() as client:
                # В README указан набор аргументов: sub_id, private_key, preshared_key, profile_id, paid_for
                user = await client.users.create(
                    sub_id=sub_id,
                    private_key=keypair.private_key,
                    preshared_key=keypair.preshared_key,
                    profile_id=pid,
                    paid_for=paid_for_dt,
                    **({"name": name} if name else {}),
                )

            logger.info(f"Created user in Amnezio: subid={subid}, profile_id={pid}, paid_for={paid_for}")
            return user
        except TypeError:
            # Если API не принимает name — повторяем без него.
            try:
                if keypair is None:
                    keypair = await self.generate_keypair()
                pid = int(profile_id if profile_id is not None else self.profile_id)
                sub_id = str(subid)
                async with self._client() as client:
                    user = await client.users.create(
                        sub_id=sub_id,
                        private_key=keypair.private_key,
                        preshared_key=keypair.preshared_key,
                        profile_id=pid,
                        paid_for=paid_for_dt,
                    )
                logger.info(f"Created user in Amnezio: subid={subid}, profile_id={pid}, paid_for={paid_for}")
                return user
            except Exception as e:
                logger.error(f"Error creating Amnezio user (fallback): subid={subid}, error={e}")
                return None
        except Exception as e:
            logger.error(f"Error creating Amnezio user: subid={subid}, error={e}")
            return None

    async def renew_subscription(self, user_id: int, paid_for: Union[date, datetime]) -> bool:
        # Нормализуем в datetime, чтобы можно было хранить точное время
        if isinstance(paid_for, date) and not isinstance(paid_for, datetime):
            paid_for_dt = datetime.combine(paid_for, time.max)
        else:
            paid_for_dt = paid_for  # type: ignore[assignment]
        try:
            async with self._client() as client:
                await client.users.renew_subscription(user_id, paid_for_dt)
            logger.info(f"Renewed Amnezio subscription: user_id={user_id}, paid_for={paid_for_dt}")
            return True
        except Exception as e:
            logger.error(f"Error renewing Amnezio subscription: user_id={user_id}, error={e}")
            return False

    async def renew_subscription_by_subid(self, subid: int, paid_for: Union[date, datetime]) -> bool:
        # Нормализуем в datetime
        if isinstance(paid_for, date) and not isinstance(paid_for, datetime):
            paid_for_dt = datetime.combine(paid_for, time.max)
        else:
            paid_for_dt = paid_for  # type: ignore[assignment]
        try:
            async with self._client() as client:
                await client.users.renew_subscription_by_sub_id(str(subid), paid_for_dt)
            logger.info(f"Renewed Amnezio by sub_id: subid={subid}, paid_for={paid_for_dt}")
            return True
        except Exception as e:
            logger.error(f"Error renewing Amnezio subscription by subid: subid={subid}, error={e}")
            return False

    async def enable_user(self, user_id: int) -> bool:
        try:
            async with self._client() as client:
                await client.users.enable(user_id)
            logger.info(f"Enabled Amnezio user: user_id={user_id}")
            return True
        except Exception as e:
            logger.error(f"Error enabling Amnezio user: user_id={user_id}, error={e}")
            return False

    async def enable_user_by_subid(self, subid: int) -> bool:
        try:
            async with self._client() as client:
                await client.users.enable_by_sub_id(str(subid))
            logger.info(f"Enabled Amnezio by sub_id: subid={subid}")
            return True
        except Exception as e:
            logger.error(f"Error enabling Amnezio user by subid: subid={subid}, error={e}")
            return False

    async def disable_user(self, user_id: int) -> bool:
        try:
            async with self._client() as client:
                await client.users.disable(user_id)
            logger.info(f"Disabled Amnezio user: user_id={user_id}")
            return True
        except Exception as e:
            logger.error(f"Error disabling Amnezio user: user_id={user_id}, error={e}")
            return False

    async def disable_user_by_subid(self, subid: int) -> bool:
        try:
            async with self._client() as client:
                await client.users.disable_by_sub_id(str(subid))
            logger.info(f"Disabled Amnezio by sub_id: subid={subid}")
            return True
        except Exception as e:
            logger.error(f"Error disabling Amnezio user by subid: subid={subid}, error={e}")
            return False

    async def delete_user(self, user_id: int) -> bool:
        try:
            async with self._client() as client:
                await client.users.delete(user_id)
            logger.info(f"Deleted Amnezio user: user_id={user_id}")
            return True
        except Exception as e:
            logger.error(f"Error deleting Amnezio user: user_id={user_id}, error={e}")
            return False

    async def delete_user_by_subid(self, subid: int) -> bool:
        try:
            async with self._client() as client:
                await client.users.delete_by_sub_id(str(subid))
            logger.info(f"Deleted Amnezio by sub_id: subid={subid}")
            return True
        except Exception as e:
            logger.error(f"Error deleting Amnezio user by subid: subid={subid}, error={e}")
            return False

    async def get_config(self, user_id: int) -> Any | None:
        """Возвращает WireGuard конфиг пользователя (как отдаёт панель)."""
        try:
            async with self._client() as client:
                return await client.users.get_config(user_id)
        except Exception as e:
            logger.error(f"Error getting Amnezio config: user_id={user_id}, error={e}")
            return None

    async def get_config_by_subid(self, subid: int) -> Any | None:
        try:
            async with self._client() as client:
                return await client.users.get_config_by_sub_id(str(subid))
        except Exception as e:
            logger.error(f"Error getting Amnezio config by subid: subid={subid}, error={e}")
            return None


_amnezio_connector: AmnezioConnector | None = None


def get_amnezio_connector() -> AmnezioConnector:
    global _amnezio_connector
    if _amnezio_connector is None:
        _amnezio_connector = AmnezioConnector()
    return _amnezio_connector

