"""
Inhibitor API client: sync and async.

Uses the permanent default URL unless base_url is overridden.
Sends only query, metadata, and additional_metadata to the inference endpoint.
"""

import httpx

from inhibitor.constants import DEFAULT_BASE_URL, INFERENCE_PATH
from inhibitor._http import detail_from_response
from inhibitor.exceptions import (
    InhibitorAPIError,
    InhibitorAuthError,
    InhibitorValidationError,
)
from inhibitor.models import InferenceRequest, InferenceResponse
from inhibitor.inference import InferenceAPI, AsyncInferenceAPI


class InhibitorClient:
    """
    Sync client for the Inhibitor policy compliance inference API.

    Authenticate with your API key. By default uses the production URL;
    pass base_url to override (e.g. for a different environment).
    """

    def __init__(
        self,
        api_key: str,
        base_url: str | None = None,
        *,
        timeout: float = 60.0,
        headers: dict[str, str] | None = None,
    ):
        """
        :param api_key: Your Inhibitor API key (required).
        :param base_url: Override API base URL. Default: production URL.
        :param timeout: Request timeout in seconds.
        :param headers: Optional extra HTTP headers.
        """
        self.api_key = api_key
        self.base_url = (base_url or DEFAULT_BASE_URL).rstrip("/")
        self.timeout = timeout
        self._headers = dict(headers or {})
        self._headers.setdefault("X-API-Key", api_key)
        self._headers.setdefault("Content-Type", "application/json")
        self._headers.setdefault("Accept", "application/json")

        self.inference = InferenceAPI(self)

    def _post_inference(self, request: InferenceRequest) -> InferenceResponse:
        """POST /v1/inference with request body only (no query params)."""
        url = f"{self.base_url}{INFERENCE_PATH}"
        with httpx.Client(timeout=self.timeout, headers=self._headers) as http:
            response = http.post(url, json=request.model_dump(mode="json"))

        return _handle_inference_response(response)


class AsyncInhibitorClient(InhibitorClient):
    """Async client. Use async with or call aclose() when done."""

    def __init__(
        self,
        api_key: str,
        base_url: str | None = None,
        *,
        timeout: float = 60.0,
        headers: dict[str, str] | None = None,
    ):
        super().__init__(
            api_key=api_key,
            base_url=base_url,
            timeout=timeout,
            headers=headers,
        )
        self.inference = AsyncInferenceAPI(self)

    async def _post_inference_async(
        self, request: InferenceRequest
    ) -> InferenceResponse:
        """POST /v1/inference (async)."""
        url = f"{self.base_url}{INFERENCE_PATH}"
        async with httpx.AsyncClient(
            timeout=self.timeout, headers=self._headers
        ) as http:
            response = await http.post(url, json=request.model_dump(mode="json"))
        return _handle_inference_response(response)

    async def aclose(self) -> None:
        """Close async resources. No-op for current implementation."""
        pass

    async def __aenter__(self) -> "AsyncInhibitorClient":
        return self

    async def __aexit__(self, *args: object) -> None:
        await self.aclose()


def _handle_inference_response(response: httpx.Response) -> InferenceResponse:
    """Map HTTP status to exceptions or return parsed InferenceResponse."""
    if response.status_code == 401:
        detail = detail_from_response(response)
        raise InhibitorAuthError(
            detail or "Invalid or expired API key", status_code=401
        )
    if response.status_code == 400:
        detail = detail_from_response(response)
        raise InhibitorValidationError(detail or "Bad request", status_code=400)
    if response.status_code == 404:
        detail = detail_from_response(response)
        raise InhibitorAPIError(
            detail or "Not found",
            status_code=404,
            detail=detail,
            response_body=response.text,
        )
    if response.status_code >= 400:
        detail = detail_from_response(response)
        raise InhibitorAPIError(
            detail or response.reason_phrase or "API error",
            status_code=response.status_code,
            detail=detail,
            response_body=response.text,
        )
    return InferenceResponse.model_validate(response.json())
