"""Base resource class for BrainstormRouter API resources."""

from __future__ import annotations

from typing import Any, Callable, Dict, Optional

import httpx


class BrainstormRouterError(Exception):
    """API error with status code and error type."""

    def __init__(self, message: str, status: int, error_type: str = "api_error"):
        super().__init__(message)
        self.status = status
        self.error_type = error_type


class Resource:
    """Base class for API resource namespaces."""

    def __init__(self, base_url: str, headers: Callable[[], Dict[str, str]]):
        self._base_url = base_url.rstrip("/")
        self._headers = headers

    def _get(self, path: str, query: Optional[Dict[str, str]] = None) -> Any:
        return self._request("GET", path, params=query)

    def _post(self, path: str, body: Optional[Any] = None) -> Any:
        return self._request("POST", path, json=body)

    def _put(self, path: str, body: Optional[Any] = None) -> Any:
        return self._request("PUT", path, json=body)

    def _patch(self, path: str, body: Optional[Any] = None) -> Any:
        return self._request("PATCH", path, json=body)

    def _delete(self, path: str) -> Any:
        return self._request("DELETE", path)

    def _request(
        self,
        method: str,
        path: str,
        params: Optional[Dict[str, str]] = None,
        json: Optional[Any] = None,
    ) -> Any:
        url = f"{self._base_url}{path}"
        headers = {"Content-Type": "application/json", **self._headers()}

        with httpx.Client(timeout=30.0) as client:
            response = client.request(
                method, url, headers=headers, params=params, json=json
            )

        if response.status_code >= 400:
            error_msg = f"HTTP {response.status_code}"
            error_type = "api_error"
            try:
                body = response.json()
                if "error" in body:
                    error_msg = body["error"].get("message", error_msg)
                    error_type = body["error"].get("type", error_type)
            except Exception:
                pass
            raise BrainstormRouterError(error_msg, response.status_code, error_type)

        return response.json()


class AsyncResource:
    """Async base class for API resource namespaces."""

    def __init__(self, base_url: str, headers: Callable[[], Dict[str, str]]):
        self._base_url = base_url.rstrip("/")
        self._headers = headers

    async def _get(self, path: str, query: Optional[Dict[str, str]] = None) -> Any:
        return await self._request("GET", path, params=query)

    async def _post(self, path: str, body: Optional[Any] = None) -> Any:
        return await self._request("POST", path, json=body)

    async def _put(self, path: str, body: Optional[Any] = None) -> Any:
        return await self._request("PUT", path, json=body)

    async def _patch(self, path: str, body: Optional[Any] = None) -> Any:
        return await self._request("PATCH", path, json=body)

    async def _delete(self, path: str) -> Any:
        return await self._request("DELETE", path)

    async def _request(
        self,
        method: str,
        path: str,
        params: Optional[Dict[str, str]] = None,
        json: Optional[Any] = None,
    ) -> Any:
        url = f"{self._base_url}{path}"
        headers = {"Content-Type": "application/json", **self._headers()}

        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.request(
                method, url, headers=headers, params=params, json=json
            )

        if response.status_code >= 400:
            error_msg = f"HTTP {response.status_code}"
            error_type = "api_error"
            try:
                body = response.json()
                if "error" in body:
                    error_msg = body["error"].get("message", error_msg)
                    error_type = body["error"].get("type", error_type)
            except Exception:
                pass
            raise BrainstormRouterError(error_msg, response.status_code, error_type)

        return response.json()
