"""HTTP transport layer with retry, timeout, and rate limiting (httpx-based)."""

from __future__ import annotations

from typing import Any

import httpx
from tenacity import (
    retry,
    retry_if_exception_type,
    stop_after_attempt,
    wait_exponential,
)

from ai4gh.core.config import Config
from ai4gh.core.exceptions import (
    NotFoundError,
    RateLimitError,
    ServerError,
    TransportError,
)


class Transport:
    """Async HTTP transport with retry, timeout, and rate limiting.

    Uses httpx for async HTTP/1.1 and HTTP/2 support.
    """

    def __init__(self, config: Config | None = None) -> None:
        self._config = config or Config()
        self._client: httpx.AsyncClient | None = None

    @property
    def client(self) -> httpx.AsyncClient:
        """Get or create the httpx async client."""
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(
                timeout=httpx.Timeout(self._config.timeout),
                verify=self._config.verify_ssl,
                follow_redirects=True,
            )
        return self._client

    async def close(self) -> None:
        """Close the HTTP client."""
        if self._client is not None and not self._client.is_closed:
            await self._client.aclose()
            self._client = None

    def _raise_for_status(self, response: httpx.Response) -> None:
        """Convert HTTP error responses to ai4gh exceptions."""
        if response.is_success:
            return

        status = response.status_code
        try:
            body = response.json()
            message = body.get("message", response.text)
        except Exception:
            message = response.text

        if status == 404:
            raise NotFoundError(message)
        if status == 429:
            retry_after = response.headers.get("Retry-After")
            raise RateLimitError(
                message,
                retry_after=float(retry_after) if retry_after else None,
            )
        if status >= 500:
            raise ServerError(message, status_code=status)
        raise TransportError(message, status_code=status)

    async def _request(
        self,
        method: str,
        url: str,
        *,
        headers: dict[str, str] | None = None,
        params: dict[str, Any] | None = None,
        json: Any = None,
        data: Any = None,
    ) -> httpx.Response:
        """Make an HTTP request with error handling."""
        try:
            response = await self.client.request(
                method,
                url,
                headers=headers,
                params=params,
                json=json,
                data=data,
            )
        except httpx.TimeoutException as e:
            raise TransportError(f"Request timed out: {e}") from e
        except httpx.ConnectError as e:
            raise TransportError(f"Connection failed: {e}") from e

        self._raise_for_status(response)
        return response

    @retry(
        retry=retry_if_exception_type((ServerError, RateLimitError)),
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=1, max=30),
        reraise=True,
    )
    async def get(
        self,
        url: str,
        *,
        headers: dict[str, str] | None = None,
        params: dict[str, Any] | None = None,
    ) -> httpx.Response:
        """HTTP GET with retry logic."""
        return await self._request("GET", url, headers=headers, params=params)

    @retry(
        retry=retry_if_exception_type((ServerError, RateLimitError)),
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=1, max=30),
        reraise=True,
    )
    async def post(
        self,
        url: str,
        *,
        headers: dict[str, str] | None = None,
        params: dict[str, Any] | None = None,
        json: Any = None,
        data: Any = None,
    ) -> httpx.Response:
        """HTTP POST with retry logic."""
        return await self._request(
            "POST", url, headers=headers, params=params, json=json, data=data
        )

    @retry(
        retry=retry_if_exception_type((ServerError, RateLimitError)),
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=1, max=30),
        reraise=True,
    )
    async def delete(
        self,
        url: str,
        *,
        headers: dict[str, str] | None = None,
    ) -> httpx.Response:
        """HTTP DELETE with retry logic."""
        return await self._request("DELETE", url, headers=headers)
