# src/veritell_langchain/client.py
from __future__ import annotations

from typing import Any, Dict, Optional

import requests

from .config import VeritellConfig, resolve_config
from .errors import (
    RateLimitedError,
    ServiceUnavailableError,
    VeritellAuthError,
    VeritellTimeoutError,
)
from .fail_open import fail_open_or_raise
from .http import post_json
from .schema import VeritellEvaluationResult


class VeritellHttpResponse:
    def __init__(
        self,
        *,
        status_code: int,
        json: Optional[Dict[str, Any]] = None,
        raw: Any = None,
    ) -> None:
        self.status_code = status_code
        self.json: Dict[str, Any] = json or {}
        self.raw = raw


class VeritellHttpClient:
    def __init__(self, config: VeritellConfig) -> None:
        self._config = config

    def _url(self, path: str) -> str:
        return self._config.base_url.rstrip("/") + "/" + path.lstrip("/")

    def _headers(self, *, accept: str = "application/json") -> Dict[str, str]:
        headers: Dict[str, str] = {
            "Accept": accept,
            "Content-Type": "application/json",
        }
        if self._config.api_key:
            headers["Authorization"] = f"Bearer {self._config.api_key}"
        return headers

    def post_json(self, path: str, payload: Dict[str, Any]) -> VeritellHttpResponse:
        url = self._url(path)
        resp = requests.post(
            url,
            json=payload,
            headers=self._headers(accept="application/json"),
            timeout=float(self._config.timeout_seconds or 30.0),
        )
        body: Dict[str, Any]
        try:
            body = resp.json() if resp.content else {}
        except ValueError:
            body = {}
        return VeritellHttpResponse(status_code=resp.status_code, json=body, raw=resp)

    def post_ndjson(self, path: str, payload: Dict[str, Any]):
        url = self._url(path)
        return requests.post(
            url,
            json=payload,
            headers=self._headers(accept="application/x-ndjson"),
            timeout=float(self._config.timeout_seconds or 30.0),
            stream=True,
        )


class VeritellClient:
    """
    Thin client wrapper used by tests:
    - uses `http.post_json` for error mapping
    - supports fail-open semantics via `fail_open_or_raise`
    """

    def __init__(
        self,
        *,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        timeout_seconds: Optional[float] = None,
        fail_open: Optional[bool] = None,
        env: Optional[str] = None,
        config_path: Optional[str] = None,
        http_client: Any = None,
    ) -> None:
        self._config: VeritellConfig = resolve_config(
            config_path=config_path,
            env=env,
            base_url=base_url,
            api_key=api_key,
            timeout_seconds=timeout_seconds,
            fail_open=fail_open,
        )
        self._http_client = http_client
        self._post_json = post_json

    def _url(self, path: str) -> str:
        return self._config.base_url.rstrip("/") + "/" + path.lstrip("/")

    def _headers(self) -> Dict[str, str]:
        headers: Dict[str, str] = {"Accept": "application/json", "Content-Type": "application/json"}
        if self._config.api_key:
            headers["Authorization"] = f"Bearer {self._config.api_key}"
        return headers

    def evaluate(
        self,
        *,
        payload: Optional[Dict[str, Any]] = None,
        prompt: Optional[str] = None,
        model_output: Optional[str] = None,
        **_: Any,
    ) -> VeritellEvaluationResult:
        if payload is None:
            payload = {}
            if prompt is not None:
                payload["prompt"] = prompt
            if model_output is not None:
                payload["model_output"] = model_output

        try:
            resp = self._post_json(
                self._http_client,
                self._url("/evaluate"),
                payload=payload,
                headers=self._headers(),
                timeout_s=float(self._config.timeout_seconds or 30.0),
            )
            return VeritellEvaluationResult.from_api(resp.json_data)
        except TimeoutError as e:
            return fail_open_or_raise(
                exc=VeritellTimeoutError(message=str(e)),
                fail_open=bool(self._config.fail_open),
            )
        except VeritellAuthError:
            raise
        except (RateLimitedError, ServiceUnavailableError, VeritellTimeoutError) as e:
            return fail_open_or_raise(exc=e, fail_open=bool(self._config.fail_open))


__all__ = [
    "VeritellClient",
    "VeritellHttpClient",
    "VeritellHttpResponse",
]
