"""Base API client with shared HTTP logic."""

from __future__ import annotations

import os
from typing import Any

import httpx


def _get_base_url() -> str:
    return os.environ.get("PEON_API_URL", "http://127.0.0.1:8420")


def _get_api_key() -> str | None:
    return os.environ.get("PEON_API_KEY") or None


class BaseAPIClient:
    """Async HTTP client base with shared request/pagination helpers."""

    def __init__(
        self,
        base_url: str | None = None,
        api_key: str | None = None,
        timeout: float = 30.0,
    ):
        self._base_url = (base_url or _get_base_url()).rstrip("/")
        self._api_key = api_key if api_key is not None else _get_api_key()
        self._timeout = timeout
        self._client: httpx.AsyncClient | None = None

    async def _get_client(self) -> httpx.AsyncClient:
        if self._client is None or self._client.is_closed:
            headers: dict[str, str] = {}
            if self._api_key:
                headers["X-API-Key"] = self._api_key
            self._client = httpx.AsyncClient(
                base_url=self._base_url,
                headers=headers,
                timeout=self._timeout,
            )
        return self._client

    async def close(self) -> None:
        if self._client and not self._client.is_closed:
            await self._client.close()

    async def _request(
        self,
        method: str,
        path: str,
        *,
        json: Any = None,
        params: dict[str, Any] | None = None,
        timeout: float | None = None,
    ) -> dict | list | str:
        """Execute an HTTP request and translate errors.

        Returns the parsed JSON body on 2xx.
        Returns an error ``str`` on 4xx/5xx (matching MCP convention).
        """
        client = await self._get_client()
        kwargs: dict[str, Any] = {}
        if json is not None:
            kwargs["json"] = json
        if params is not None:
            # Strip None values so httpx doesn't send "param=None"
            kwargs["params"] = {k: v for k, v in params.items() if v is not None}
        if timeout is not None:
            kwargs["timeout"] = timeout

        resp = await client.request(method, path, **kwargs)

        if resp.is_success:
            return resp.json()

        # Error handling: try to extract {"error": "..."} from response
        try:
            body = resp.json()
            if isinstance(body, dict) and "error" in body:
                return body["error"]
        except Exception:
            pass
        return f"HTTP {resp.status_code}: {resp.text}"

    async def _paginate_all(
        self,
        path: str,
        params: dict[str, Any] | None = None,
    ) -> list[dict] | str:
        """Collect all pages from a paginated endpoint."""
        params = dict(params or {})
        params.setdefault("limit", 200)
        params["offset"] = 0
        all_items: list[dict] = []

        while True:
            result = await self._request("GET", path, params=params)
            if isinstance(result, str):
                return result  # error
            items = result.get("items", [])
            all_items.extend(items)
            if not result.get("has_next", False):
                break
            params["offset"] += len(items)

        return all_items
