"""Base httpx async client for Stock Oracle."""

from __future__ import annotations

from typing import Any

import httpx

from libs.common.retries import with_retry
from libs.oracle_client.exceptions import (
    OracleClientError,
    OracleConnectionError,
    OracleNotFoundError,
    OracleServerError,
    OracleTimeoutError,
)


class OracleClient:
    """Async HTTP client wrapping Stock Oracle REST API."""

    def __init__(self, base_url: str, timeout: float = 30.0) -> None:
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout
        self._client: httpx.AsyncClient | None = None

    async def __aenter__(self) -> OracleClient:
        self._client = httpx.AsyncClient(
            base_url=self._base_url,
            timeout=self._timeout,
        )
        return self

    async def __aexit__(self, *args: Any) -> None:
        if self._client:
            await self._client.aclose()
            self._client = None

    def _ensure_client(self) -> httpx.AsyncClient:
        if self._client is None:
            raise RuntimeError("OracleClient must be used as async context manager.")
        return self._client

    @with_retry(max_attempts=3, min_wait=0.1, max_wait=5.0, multiplier=0.1)
    async def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
        client = self._ensure_client()
        try:
            response = await client.get(path, params=params)
        except httpx.ConnectError as exc:
            raise OracleConnectionError(str(exc), source="oracle", entity=path) from exc
        except httpx.TimeoutException as exc:
            raise OracleTimeoutError(str(exc), source="oracle", entity=path) from exc

        return self._handle_response(response, path)

    @with_retry(max_attempts=3, min_wait=0.1, max_wait=5.0, multiplier=0.1)
    async def post(
        self,
        path: str,
        json: dict[str, Any] | None = None,
        params: dict[str, Any] | None = None,
    ) -> Any:
        client = self._ensure_client()
        try:
            response = await client.post(path, json=json, params=params)
        except httpx.ConnectError as exc:
            raise OracleConnectionError(str(exc), source="oracle", entity=path) from exc
        except httpx.TimeoutException as exc:
            raise OracleTimeoutError(str(exc), source="oracle", entity=path) from exc

        return self._handle_response(response, path)

    def _handle_response(self, response: httpx.Response, path: str) -> Any:
        if response.status_code == 404:
            raise OracleNotFoundError(
                f"Not found: {path}",
                source="oracle",
                entity=path,
                context={"status_code": 404},
            )
        if response.status_code >= 500:
            raise OracleServerError(
                f"Server error {response.status_code}: {path}",
                source="oracle",
                entity=path,
                context={"status_code": response.status_code},
            )
        if response.status_code >= 400:
            raise OracleClientError(
                f"Client error {response.status_code}: {path}",
                source="oracle",
                entity=path,
                context={"status_code": response.status_code},
            )
        return response.json()

    async def health_check(self) -> bool:
        try:
            data = await self.get("/health")
            return isinstance(data, dict) and data.get("status") == "ok"
        except Exception:
            return False


def make_oracle_client() -> OracleClient:
    from libs.common.config import get_settings

    s = get_settings()
    return OracleClient(base_url=s.stock_oracle_url, timeout=float(s.stock_oracle_timeout))
