from __future__ import annotations

import logging
import os
import time
from typing import Any

import httpx

logger = logging.getLogger(__name__)


class BenchmarkCommandCenter:
    """Thin HTTP client for reporting benchmark results to the command center.

    Never raises exceptions that would crash the benchmark runner.
    All errors are logged as warnings.
    """

    def __init__(
        self,
        base_url: str | None = None,
        api_key: str | None = None,
        timeout: float = 30.0,
        max_retries: int = 3,
    ):
        self._base_url = (base_url or os.environ.get("BCC_BASE_URL", "")).rstrip("/")
        self._api_key = api_key or os.environ.get("BCC_API_KEY", "")
        self._timeout = timeout
        self._max_retries = max_retries
        self._enabled = bool(self._base_url and self._api_key)

        if not self._enabled:
            logger.info(
                "Benchmark Command Center SDK disabled (no base_url or api_key)"
            )

    @property
    def enabled(self) -> bool:
        return self._enabled

    def _headers(self) -> dict[str, str]:
        return {
            "Authorization": f"Bearer {self._api_key}",
            "Content-Type": "application/json",
        }

    def _request(self, method: str, path: str, **kwargs: Any) -> dict[str, Any] | None:
        if not self._enabled:
            return None

        url = f"{self._base_url}{path}"
        for attempt in range(self._max_retries):
            try:
                with httpx.Client(timeout=self._timeout) as client:
                    response = client.request(
                        method, url, headers=self._headers(), **kwargs
                    )
                    if response.status_code < 500:
                        response.raise_for_status()
                        return response.json()
                    logger.warning(
                        "BCC %s %s returned %d, retrying (%d/%d)",
                        method,
                        path,
                        response.status_code,
                        attempt + 1,
                        self._max_retries,
                    )
            except (httpx.ConnectError, httpx.TimeoutException) as e:
                logger.warning(
                    "BCC connection error for %s %s: %s, retrying (%d/%d)",
                    method,
                    path,
                    e,
                    attempt + 1,
                    self._max_retries,
                )
            except httpx.HTTPStatusError as e:
                logger.warning("BCC HTTP error for %s %s: %s", method, path, e)
                return None
            except Exception as e:
                logger.warning("BCC unexpected error for %s %s: %s", method, path, e)
                return None

            time.sleep(min(2**attempt, 8))

        logger.warning(
            "BCC %s %s failed after %d retries", method, path, self._max_retries
        )
        return None

    def create_run(
        self,
        benchmark_name: str,
        benchmark_type: str,
        params: dict[str, Any] | None = None,
        strategy: str | None = None,
        git_sha: str | None = None,
        tags: list[str] | None = None,
        mlflow_run_id: str | None = None,
    ) -> str | None:
        """Create a new benchmark run. Returns run_id string or None on failure."""
        result = self._request(
            "POST",
            "/api/v1/runs",
            json={
                "benchmark_name": benchmark_name,
                "benchmark_type": benchmark_type,
                "params": params,
                "strategy": strategy,
                "git_sha": git_sha,
                "tags": tags,
                "mlflow_run_id": mlflow_run_id,
            },
        )
        return result["run_id"] if result else None

    def report_result(
        self,
        run_id: str,
        case_id: str,
        score: float,
        evaluations: list[dict[str, Any]] | None = None,
        inputs: dict[str, Any] | None = None,
        outputs: dict[str, Any] | None = None,
        duration_ms: int | None = None,
        token_usage: dict[str, Any] | None = None,
    ) -> bool:
        """Report a single benchmark result. Returns True on success."""
        result = self._request(
            "POST",
            f"/api/v1/runs/{run_id}/results",
            json={
                "case_id": case_id,
                "score": score,
                "evaluations": evaluations,
                "inputs": inputs,
                "outputs": outputs,
                "duration_ms": duration_ms,
                "token_usage": token_usage,
            },
        )
        return result is not None

    def complete_run(
        self,
        run_id: str,
        aggregate_metrics: dict[str, Any] | None = None,
        status: str = "completed",
    ) -> bool:
        """Finalize a benchmark run. Returns True on success."""
        result = self._request(
            "POST",
            f"/api/v1/runs/{run_id}/complete",
            json={
                "aggregate_metrics": aggregate_metrics,
                "status": status,
            },
        )
        return result is not None
