"""HTTP transport client using httpx.

Implements the APIClient protocol with httpx.AsyncClient for token-based auth.
"""

from typing import Any

import httpx

from ..errors import (
    AuthenticationFailed,
    NetworkError,
    NotFound,
    RequestFailed,
)
from ._transport import APIResponse


class HttpClient:
    """HTTP client implementing APIClient protocol via httpx."""

    def __init__(self, token: str, platform: str, auth_header: str = "Authorization"):
        """
        Args:
            token: API token for authentication.
            platform: Platform name for error messages ("GitHub" or "GitLab").
            auth_header: Header name for auth. "Authorization" for GitHub (Bearer),
                         "PRIVATE-TOKEN" for GitLab.
        """
        self.platform = platform
        self._token = token
        self._auth_header = auth_header
        self._client = httpx.AsyncClient(follow_redirects=True)

    def _auth_headers(self) -> dict[str, str]:
        """Build auth headers based on platform convention."""
        if self._auth_header == "PRIVATE-TOKEN":
            return {"PRIVATE-TOKEN": self._token}
        return {"Authorization": f"Bearer {self._token}"}

    def _merge_headers(self, headers: dict[str, str] | None) -> dict[str, str]:
        """Merge auth headers with caller-provided headers."""
        merged = self._auth_headers()
        if headers:
            merged.update(headers)
        return merged

    async def get(
        self,
        url: str,
        headers: dict[str, str] | None = None,
        params: dict[str, Any] | None = None,
        timeout: float | None = None,
    ) -> APIResponse:
        """Make GET request with error handling."""
        try:
            kwargs: dict[str, Any] = {
                "headers": self._merge_headers(headers),
                "params": params,
            }
            if timeout is not None:
                kwargs["timeout"] = timeout
            response = await self._client.get(url, **kwargs)
            self._check_response(response)
            return self._to_api_response(response)
        except httpx.RequestError as e:
            raise NetworkError(str(e)) from e

    async def post(
        self,
        url: str,
        headers: dict[str, str] | None = None,
        json: dict[str, Any] | None = None,
    ) -> APIResponse:
        """Make POST request with error handling."""
        try:
            response = await self._client.post(url, headers=self._merge_headers(headers), json=json)
            self._check_response(response)
            return self._to_api_response(response)
        except httpx.RequestError as e:
            raise NetworkError(str(e)) from e

    async def close(self) -> None:
        """Close the underlying httpx client."""
        await self._client.aclose()

    def _check_response(self, response: httpx.Response) -> None:
        """Check response status and raise appropriate errors."""
        if response.status_code in (401, 403):
            raise AuthenticationFailed(f"{self.platform} authentication failed: {response.text}")
        elif response.status_code == 404:
            raise NotFound("Resource", "not found")
        elif not response.is_success:
            raise RequestFailed(response.status_code, response.text)

    @staticmethod
    def _to_api_response(response: httpx.Response) -> APIResponse:
        """Convert httpx.Response to APIResponse."""
        return APIResponse(
            status_code=response.status_code,
            _body=response.content,
            headers=dict(response.headers),
        )


# Keep backward compatibility alias
HTTPClient = HttpClient


class PaginationHelper:
    """Helper for paginating API requests."""

    @staticmethod
    async def fetch_all_pages(
        fetch_page_func,
        *args,
        per_page: int = 100,
        use_header_pagination: bool = False,
        **kwargs,
    ):
        """
        Fetch all pages from a paginated endpoint.

        Args:
            fetch_page_func: Async function to fetch a single page
            *args: Arguments to pass to fetch_page_func
            per_page: Items per page
            use_header_pagination: If True, uses header-based pagination (GitLab style)
            **kwargs: Keyword arguments to pass to fetch_page_func
        """
        all_items = []
        page = 1

        while True:
            if use_header_pagination:
                items, has_next = await fetch_page_func(*args, page, **kwargs)
                all_items.extend(items)
                if not has_next:
                    break
            else:
                items = await fetch_page_func(*args, page, **kwargs)
                all_items.extend(items)
                if len(items) < per_page:
                    break

            page += 1

        return all_items
