"""Async HTTP client for the Metabase REST API."""

from __future__ import annotations

from typing import Any

import httpx

from metaverse.config import settings

# Raw response type returned by all Metabase API endpoints.
ApiResponse = dict[str, Any] | list[Any]

_client: httpx.AsyncClient | None = None


def get_client() -> httpx.AsyncClient:
    """Return the shared async HTTP client (lazy-initialised)."""
    global _client
    if _client is None:
        _client = httpx.AsyncClient(
            base_url=f"{settings.url.rstrip('/')}/api",
            headers={"X-API-KEY": settings.api_key},
            timeout=30.0,
        )
    return _client


async def close_client() -> None:
    global _client
    if _client is not None:
        await _client.aclose()
        _client = None


class MetabaseError(Exception):
    """Raised when Metabase returns an unexpected or malformed response."""


async def _raise_for_status(response: httpx.Response) -> None:
    if response.is_error:
        try:
            body = response.json()
            detail = body.get("message") or response.text
        except (ValueError, TypeError):
            detail = response.text
        raise MetabaseError(f"Metabase API error {response.status_code}: {detail}")


def as_dict(data: ApiResponse) -> dict[str, Any]:
    """Narrow an API response to a dict, raising MetabaseError if it is not."""
    if not isinstance(data, dict):
        raise MetabaseError(f"Expected a JSON object from Metabase, got {type(data).__name__}")
    return data


def as_list(data: ApiResponse, key: str = "data") -> list[Any]:
    """Normalise a flat-list or paginated-dict response into a plain list.

    Metabase sometimes wraps list responses as ``{"data": [...], "total": N}``;
    this helper unwraps either form transparently.
    """
    if isinstance(data, list):
        return data
    if isinstance(data, dict):
        return data.get(key, [])
    return []


async def get(path: str, **kwargs: Any) -> ApiResponse:
    r = await get_client().get(path, **kwargs)
    await _raise_for_status(r)
    return r.json()  # type: ignore[return-value]


async def post(path: str, json: dict[str, Any], **kwargs: Any) -> ApiResponse:
    r = await get_client().post(path, json=json, **kwargs)
    await _raise_for_status(r)
    return r.json()  # type: ignore[return-value]


async def put(path: str, json: dict[str, Any], **kwargs: Any) -> ApiResponse:
    r = await get_client().put(path, json=json, **kwargs)
    await _raise_for_status(r)
    return r.json()  # type: ignore[return-value]


async def delete(path: str, **kwargs: Any) -> None:
    r = await get_client().delete(path, **kwargs)
    await _raise_for_status(r)
