from __future__ import annotations

import logging
from typing import Any, Dict, List

import httpx

from .auth import LoyaAuthenticator

logger = logging.getLogger(__name__)


class LoyaClient:
    """
    Модульный асинхронный клиент для Loya API
    Полностью разделён на: HTTP, авторизацию, ошибки, утилиты
    """

    def __init__(
        self,
        base_url: str,
        email: str,
        password_hash: str,
        apikey: str,
        timeout: float = 20.0,
        user_agent: str = "LoyaClient/1.0",
    ):
        self.base_url = base_url.rstrip("/")

        limits = httpx.Limits(max_keepalive_connections=10, max_connections=50)
        self._http = httpx.AsyncClient(
            base_url=self.base_url,
            timeout=httpx.Timeout(timeout),
            limits=limits,
            headers={"User-Agent": user_agent, "Accept": "application/json"},
        )

        self.auth = LoyaAuthenticator(
            client=self._http,
            email=email,
            password_hash=password_hash,
            apikey=apikey,
        )

    # === Базовые HTTP-методы ===
    async def _request(
            self,
            method: str,
            path: str,
            **kwargs: Any
    ) -> httpx.Response:
        await self.auth.ensure_auth()
        response = await self._http.request(method, path, **kwargs)

        # Обновляем сессию, если пришла новая кука
        if "Set-Cookie" in response.headers:
            self.auth.update_from_set_cookie(response.headers["Set-Cookie"])

        # Авторелогин при 401
        if response.status_code == 401:
            logger.warning("401 — сессия истекла, повторная авторизация")
            self.auth.clear()
            await self.auth.ensure_auth()
            response = await self._http.request(method, path, **kwargs)

        response.raise_for_status()
        return response

    async def get(
            self,
            path: str,
            **kwargs: Any
    ) -> httpx.Response:
        return await self._request("GET", path, **kwargs)

    async def post_json(
            self,
            path: str,
            data: Any = None,
            **kwargs: Any
    ) -> httpx.Response:
        headers = kwargs.pop("headers", {})
        headers.setdefault("Content-Type", "application/json")

        return await self._request("POST", path, json=data, headers=headers, **kwargs)

    async def put_json(
            self,
            path: str,
            data: Any = None,
            **kwargs: Any,
    ) -> httpx.Response:
        headers = kwargs.pop("headers", {})
        headers.setdefault("Content-Type", "application/json")

        return await self.request("PUT", path, json=data, headers=headers, **kwargs)

    async def delete(
            self,
            path: str,
            **kwargs: Any
    ) -> httpx.Response:
        return await self.request("DELETE", path, **kwargs)

    # === API методы ===
    async def get_card_by_number(
            self,
            card_number: str
    ) -> Dict[str, Any]:
        return (await self.get(f"/api/1.0/card/number/{card_number}")).json()

    async def get_attached_clients(
            self,
            card_numbers: List[str]
    ) -> List[Dict[str, Any]]:
        return (await self.post_json("/api/1.0/card/attached/clients", data=card_numbers)).json()

    async def get_locations(self) -> Dict[str, Any]:
        return (await self.get("/api/1.0/location")).json()

    # === Жизненный цикл ===
    async def close(self) -> None:
        await self._http.aclose()

    async def __aenter__(self) -> "LoyaClient":
        return self

    async def __aexit__(self, exc_type, exc, tb) -> None:
        await self.close()
