"""Asynchronous HTTP client."""

import dataclasses
from typing import Protocol

import httpx

from mariner import exceptions


@dataclasses.dataclass(frozen=True)
class ClientConfig:
    """Configuration for the HTTP client."""

    timeout: int = 10
    follow_redirects: bool = True


class AsyncClientProtocol(Protocol):
    """Protocol for an asynchronous HTTP client."""

    async def close(self) -> None:
        """Close the client and release any resources."""

    async def get(self, url: str) -> str:
        """Perform a GET request and return text.

        Args:
            url: The URL to send the GET request to.

        """

    async def get_bytes(self, url: str) -> bytes:
        """Perform a GET request and return raw bytes.

        Args:
            url: The URL to send the GET request to.

        """


class _HttpTransport(Protocol):
    """Internal protocol for the raw HTTP transport. Not exported."""

    async def get(self, url: str, **kwargs) -> httpx.Response: ...

    async def aclose(self) -> None: ...


class AsyncClient:
    """Asynchronous HTTP client using httpx."""

    def __init__(
        self,
        config: ClientConfig | None = None,
        *,
        _transport: _HttpTransport | None = None,
    ) -> None:
        cfg = config or ClientConfig()
        self._client = _transport or httpx.AsyncClient(
            follow_redirects=cfg.follow_redirects,
            timeout=cfg.timeout,
        )

    async def close(self):
        await self._client.aclose()

    async def get(self, url: str) -> str:
        try:
            response = await self._client.get(url)
            response.raise_for_status()
        except httpx.HTTPError as e:
            raise exceptions.HTTPError(url, e) from e
        return response.text

    async def get_bytes(self, url: str) -> bytes:
        try:
            response = await self._client.get(url)
            response.raise_for_status()
        except httpx.HTTPError as e:
            raise exceptions.HTTPError(url, e) from e
        return response.content

    async def __aenter__(self):
        return self

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