from __future__ import annotations

import asyncio

import structlog
import httpx

from aegro.errors import AegroAPIError, translate_api_error

logger = structlog.get_logger()


class AegroClient:
    def __init__(
        self,
        base_url: str = "https://app.aegro.com.br",
        timeout: float = 30.0,
        max_retries: int = 3,
    ):
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout
        self._max_retries = max_retries
        self._http: httpx.AsyncClient | None = None

    def _get_http(self) -> httpx.AsyncClient:
        if self._http is None or self._http.is_closed:
            self._http = httpx.AsyncClient(timeout=self._timeout)
        return self._http

    async def close(self) -> None:
        if self._http is not None and not self._http.is_closed:
            await self._http.aclose()
            self._http = None

    def _headers(self, api_key: str) -> dict[str, str]:
        return {
            "Aegro-Public-API-Key": api_key,
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

    async def _request(
        self,
        method: str,
        path: str,
        api_key: str,
        json_body: dict | None = None,
        *,
        raw: bool = False,
    ) -> dict | list | bytes | None:
        url = f"{self._base_url}{path}"
        attempt = 0
        last_response = None
        http = self._get_http()

        while attempt <= self._max_retries:
            try:
                response = await http.request(
                    method, url, headers=self._headers(api_key), json=json_body
                )
                last_response = response
                if response.status_code == 204:
                    return None
                if response.status_code in (200, 201):
                    if raw:
                        return response.content
                    return response.json()
                if response.status_code >= 500 and attempt < self._max_retries:
                    attempt += 1
                    logger.warning(
                        "aegro_api_retry", path=path, status=response.status_code, attempt=attempt
                    )
                    await asyncio.sleep(2**attempt)
                    continue
                body = {}
                try:
                    body = response.json()
                except Exception:
                    pass
                raise translate_api_error(response.status_code, body)
            except httpx.TimeoutException:
                if attempt < self._max_retries:
                    attempt += 1
                    logger.warning("aegro_api_timeout", path=path, attempt=attempt)
                    await asyncio.sleep(2**attempt)
                    continue
                raise AegroAPIError(
                    504, "Timeout ao conectar com o servico Aegro. Tente novamente.", retryable=True
                )
            except AegroAPIError:
                raise
            except httpx.HTTPError as exc:
                raise AegroAPIError(
                    502,
                    f"Erro de conexao com o servico Aegro: {type(exc).__name__}",
                    retryable=True,
                ) from exc

        if last_response is not None:
            body = {}
            try:
                body = last_response.json()
            except Exception:
                pass
            raise translate_api_error(last_response.status_code, body)
        raise AegroAPIError(502, "Erro inesperado de conexao.", retryable=True)

    async def get(self, path: str, *, api_key: str) -> dict | list | None:
        return await self._request("GET", path, api_key)

    async def post(
        self, path: str, *, api_key: str, json_body: dict | None = None
    ) -> dict | list | None:
        return await self._request("POST", path, api_key, json_body)

    async def put(
        self, path: str, *, api_key: str, json_body: dict | None = None
    ) -> dict | list | None:
        return await self._request("PUT", path, api_key, json_body)

    async def delete(self, path: str, *, api_key: str) -> dict | list | None:
        return await self._request("DELETE", path, api_key)

    async def download(
        self,
        path: str,
        *,
        api_key: str,
        method: str = "GET",
        json_body: dict | None = None,
    ) -> bytes | None:
        """Fetch a binary payload (e.g. XML or ZIP attachment)."""
        result = await self._request(method, path, api_key, json_body, raw=True)
        if result is None:
            return None
        return result  # type: ignore[return-value]

    async def get_user_identity(self, api_key: str) -> str | None:
        """Return the email address associated with the given API key.

        Calls ``/pub/v1/users/me`` and extracts the ``email`` field.
        Returns ``None`` when the endpoint does not return a usable email.
        """
        try:
            data = await self.get("/pub/v1/users/me", api_key=api_key)
            if isinstance(data, dict):
                return data.get("email")
        except (AegroAPIError, httpx.HTTPError):
            logger.warning("identity_lookup_failed", exc_info=True)
        return None
