"""API для работы с нодами."""

from typing import TYPE_CHECKING, Optional

from .models import Node, NodeCreated
from .exceptions import NotFoundError, APIError

if TYPE_CHECKING:
    from .client import AmnezioClient


class NodesAPI:
    """API для работы с нодами."""

    def __init__(self, client: "AmnezioClient"):
        self.client = client

    async def list(self, profile_id: Optional[int] = None) -> list[Node]:
        """
        Получить список нод.

        Args:
            profile_id: Фильтр по профилю (опционально; если панель не поддерживает — игнорируется)

        Returns:
            Список нод
        """
        params = {}
        if profile_id is not None:
            params["profile_id"] = profile_id

        response = await self.client.request("GET", "/api/nodes", params=params)

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return [Node(**item) for item in response.json()]

    async def create(
        self,
        name: str,
        host: str,
        port: int = 9999,
        node_type: str = "awg",
        profile_id: Optional[int] = None,
    ) -> NodeCreated:
        """
        Создать ноду.

        Args:
            name: Название ноды
            host: Хост ноды (IP или домен)
            port: Порт HTTPS-агента (mTLS), NODE_PORT в docker-compose
            node_type: ``awg`` — AmneziaWG (amnezio-node), ``wg`` — обычный WireGuard (amnezio-node-wg)
            profile_id: ID профиля

        Returns:
            Данные ноды с ``secret_key`` и ``docker_compose``
        """
        if node_type not in ("awg", "wg"):
            raise ValueError("node_type must be 'awg' or 'wg'")

        data: dict = {
            "name": name,
            "host": host,
            "port": port,
            "node_type": node_type,
        }

        if profile_id is not None:
            data["profile_id"] = profile_id

        response = await self.client.request("POST", "/api/nodes", json=data)

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return NodeCreated(**response.json())

    async def get(self, node_id: int) -> Node:
        """
        Получить ноду по ID.

        Args:
            node_id: ID ноды

        Returns:
            Нода
        """
        response = await self.client.request("GET", f"/api/nodes/{node_id}")

        if response.status_code == 404:
            raise NotFoundError(f"Нода {node_id} не найдена")

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return Node(**response.json())

    async def update(
        self,
        node_id: int,
        name: Optional[str] = None,
        host: Optional[str] = None,
        port: Optional[int] = None,
        node_type: Optional[str] = None,
        profile_id: Optional[int] = None,
    ) -> Node:
        """
        Обновить ноду.

        Args:
            node_id: ID ноды
            name: Название ноды
            host: Хост ноды
            port: Порт агента
            node_type: ``awg`` или ``wg``
            profile_id: ID профиля

        Returns:
            Обновленная нода
        """
        if node_type is not None and node_type not in ("awg", "wg"):
            raise ValueError("node_type must be 'awg' or 'wg'")

        data: dict = {}

        if name is not None:
            data["name"] = name
        if host is not None:
            data["host"] = host
        if port is not None:
            data["port"] = port
        if node_type is not None:
            data["node_type"] = node_type
        if profile_id is not None:
            data["profile_id"] = profile_id

        response = await self.client.request("PUT", f"/api/nodes/{node_id}", json=data)

        if response.status_code == 404:
            raise NotFoundError(f"Нода {node_id} не найдена")

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return Node(**response.json())

    async def delete(self, node_id: int) -> dict:
        """
        Удалить ноду.

        Args:
            node_id: ID ноды

        Returns:
            Статус операции
        """
        response = await self.client.request("DELETE", f"/api/nodes/{node_id}")

        if response.status_code == 404:
            raise NotFoundError(f"Нода {node_id} не найдена")

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return response.json()

    async def push_config(self, node_id: int) -> dict:
        """
        Отправить конфиг профиля на ноду (POST /api/nodes/{id}/push).

        Args:
            node_id: ID ноды

        Returns:
            Статус операции
        """
        response = await self.client.request("POST", f"/api/nodes/{node_id}/push")

        if response.status_code == 404:
            raise NotFoundError(f"Нода {node_id} не найдена")

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return response.json()

    async def restart(self, node_id: int) -> dict:
        """
        Перезапустить VPN-интерфейс на ноде (POST /restart на агенте).

        Args:
            node_id: ID ноды

        Returns:
            Статус операции
        """
        response = await self.client.request("POST", f"/api/nodes/{node_id}/restart")

        if response.status_code == 404:
            raise NotFoundError(f"Нода {node_id} не найдена")

        if response.status_code != 200:
            raise APIError(response.status_code, response.text)

        return response.json()
