"""
BiasClear Client

Typed Python client for the BiasClear API.

Usage:
    from biasclear_client import Client

    bc = Client(base_url="http://localhost:8100", api_key="sk-...")
    result = bc.scan("Everyone agrees this is settled law.", domain="legal")
    print(result.truth_score, result.flags)
"""

from __future__ import annotations

import httpx
from typing import Optional

from .exceptions import AuthError, RateLimitError, ServerError, BiasClearError
from .models import ScanResult, CorrectionResult, PatternsResult, AuditResult


class Client:
    """
    BiasClear API client.

    Args:
        base_url: API server URL (default: http://localhost:8100)
        api_key: Bearer token for auth. If None, sends unauthenticated requests.
        timeout: Request timeout in seconds (default: 60 for LLM operations).
    """

    def __init__(
        self,
        base_url: str = "http://localhost:8100",
        api_key: str | None = None,
        timeout: float = 60.0,
    ):
        self._base_url = base_url.rstrip("/")
        self._api_key = api_key
        headers: dict[str, str] = {}
        if api_key:
            headers["Authorization"] = f"Bearer {api_key}"
        self._http = httpx.Client(
            base_url=self._base_url,
            headers=headers,
            timeout=timeout,
        )

    def close(self) -> None:
        """Close the underlying HTTP connection."""
        self._http.close()

    def __enter__(self) -> Client:
        return self

    def __exit__(self, *args: object) -> None:
        self.close()

    # ── Response handling ────────────────────────────────────────────

    def _handle_response(self, response: httpx.Response) -> dict:
        """
        Check response status and raise typed exceptions for errors.
        Returns parsed JSON on success.
        """
        if response.status_code == 401:
            detail = response.json().get("detail", "Authentication failed.")
            raise AuthError(detail)

        if response.status_code == 429:
            detail = response.json().get("detail", "Rate limit exceeded.")
            retry_after = response.headers.get("retry-after")
            raise RateLimitError(
                detail,
                retry_after=int(retry_after) if retry_after else None,
            )

        if response.status_code == 422:
            detail = str(response.json().get("detail", "Validation error."))
            raise BiasClearError(detail, status_code=422)

        if response.status_code >= 500:
            detail = response.json().get("detail", "Server error.")
            raise ServerError(detail, status_code=response.status_code)

        if response.status_code >= 400:
            detail = response.json().get("detail", f"Error {response.status_code}")
            raise BiasClearError(detail, status_code=response.status_code)

        return response.json()

    # ── API Methods ──────────────────────────────────────────────────

    def health(self) -> dict:
        """
        Check API health.

        Returns:
            Dict with status, biasclear version, audit_entries count.
        """
        r = self._http.get("/health")
        return self._handle_response(r)

    def scan(
        self,
        text: str,
        domain: str = "general",
        mode: str = "local",
    ) -> ScanResult:
        """
        Scan text for PIT distortions.

        Args:
            text: Text to analyze.
            domain: Domain context — "general", "legal", "media", "financial".
            mode: "local" (free, instant) or "full" (LLM-powered).

        Returns:
            ScanResult with truth_score, flags, severity, etc.

        Raises:
            AuthError: Invalid or missing API key.
            RateLimitError: Rate limit exceeded.
            BiasClearError: Validation or server error.
        """
        r = self._http.post("/scan", json={
            "text": text,
            "domain": domain,
            "mode": mode,
        })
        data = self._handle_response(r)
        return ScanResult.from_dict(data)

    def correct(
        self,
        text: str,
        domain: str = "legal",
    ) -> CorrectionResult:
        """
        Correct bias in text.

        Runs a full scan, then iterative LLM correction with
        post-correction verification.

        Args:
            text: Biased text to correct.
            domain: Domain context.

        Returns:
            CorrectionResult with corrected_text, diff_spans, score changes.

        Raises:
            AuthError: Invalid or missing API key.
            RateLimitError: Rate limit exceeded.
            BiasClearError: Validation or server error.
        """
        r = self._http.post("/correct", json={
            "text": text,
            "domain": domain,
        })
        data = self._handle_response(r)
        return CorrectionResult.from_dict(data)

    def patterns(self, domain: str = "general") -> PatternsResult:
        """
        Get active detection patterns.

        Args:
            domain: Domain filter — "general", "legal", "media", "financial".

        Returns:
            PatternsResult with frozen_patterns, learned_patterns, counts.
        """
        r = self._http.get("/patterns", params={"domain": domain})
        data = self._handle_response(r)
        return PatternsResult.from_dict(data)

    def audit(
        self,
        limit: int = 20,
        event_type: Optional[str] = None,
    ) -> AuditResult:
        """
        Get audit chain entries.

        Args:
            limit: Max entries to return.
            event_type: Filter by event type (e.g., "scan_local", "correction").

        Returns:
            AuditResult with entries, chain integrity, counts.
        """
        params: dict[str, str | int] = {"limit": limit}
        if event_type:
            params["event_type"] = event_type
        r = self._http.get("/audit", params=params)
        data = self._handle_response(r)
        return AuditResult.from_dict(data)
