"""Thin async HTTP client wrapping the ACC C2C external API (/c2c-ext)."""

from __future__ import annotations

import atexit
import asyncio
from typing import Any

import httpx


class ACCError(Exception):
    """Raised when the ACC API returns a non-2xx response."""

    def __init__(self, status: int, detail: str):
        self.status = status
        self.detail = detail
        super().__init__(f"ACC API {status}: {detail}")


class ACCClient:
    """Reusable async HTTP client for /c2c-ext endpoints."""

    def __init__(
        self,
        base_url: str,
        api_key: str,
        timeout: float = 30.0,
        verify_ssl: bool = True,
    ):
        self._base = base_url.rstrip("/")
        self._timeout = timeout
        self._http = httpx.AsyncClient(
            timeout=self._timeout,
            verify=verify_ssl,
            headers={"X-API-Key": api_key},
        )
        atexit.register(self._sync_close)

    def _sync_close(self):
        try:
            loop = asyncio.get_event_loop()
            if loop.is_running():
                loop.create_task(self._http.aclose())
            else:
                loop.run_until_complete(self._http.aclose())
        except Exception:
            pass

    async def close(self):
        await self._http.aclose()

    def _url(self, path: str) -> str:
        return f"{self._base}/c2c-ext{path}"

    async def _request(
        self,
        method: str,
        path: str,
        *,
        params: dict[str, Any] | None = None,
        json: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        resp = await self._http.request(
            method, self._url(path),
            params=params,
            json=json,
        )
        if resp.status_code >= 400:
            try:
                body = resp.json()
                detail = body.get("detail", body) if isinstance(body, dict) else body
            except Exception:
                detail = resp.text or resp.reason_phrase
            raise ACCError(resp.status_code, str(detail))
        return resp.json()

    # ── Protocol ──

    async def get_protocol(self, pipeline_id: str | None = None) -> dict:
        params = {}
        if pipeline_id:
            params["pipelineId"] = pipeline_id
        return await self._request("GET", "/protocol", params=params)

    # ── Dispatch CRUD ──

    async def create_dispatch(self, body: dict[str, Any]) -> dict:
        return await self._request("POST", "/dispatch", json=body)

    async def list_dispatches(
        self,
        *,
        pipeline_id: str | None = None,
        status: str | None = None,
        page: int = 1,
        page_size: int = 20,
    ) -> dict:
        params: dict[str, Any] = {"page": page, "pageSize": page_size}
        if pipeline_id:
            params["pipelineId"] = pipeline_id
        if status:
            params["status"] = status
        return await self._request("GET", "/dispatches", params=params)

    async def get_dispatch(self, dispatch_id: str) -> dict:
        return await self._request("GET", f"/dispatches/{dispatch_id}")

    # ── Lifecycle ──

    async def accept(self, dispatch_id: str, *, message: str | None = None, eta: str | None = None) -> dict:
        body: dict[str, Any] = {}
        if message:
            body["message"] = message
        if eta:
            body["estimatedCompletion"] = eta
        return await self._request("POST", f"/dispatches/{dispatch_id}/accept", json=body or None)

    async def reject(self, dispatch_id: str, reason: str, *, suggest_target: str | None = None) -> dict:
        body: dict[str, Any] = {"reason": reason}
        if suggest_target:
            body["suggestTarget"] = suggest_target
        return await self._request("POST", f"/dispatches/{dispatch_id}/reject", json=body)

    async def report(
        self,
        dispatch_id: str,
        *,
        report_type: str = "progress",
        content: str = "",
        progress_pct: int | None = None,
        artifacts: dict | None = None,
    ) -> dict:
        body: dict[str, Any] = {"reportType": report_type, "content": content}
        if progress_pct is not None:
            body["progressPct"] = progress_pct
        if artifacts:
            body["artifacts"] = artifacts
        return await self._request("POST", f"/dispatches/{dispatch_id}/report", json=body)

    async def complete(self, dispatch_id: str, content: str = "", *, artifacts: dict | None = None) -> dict:
        body: dict[str, Any] = {"content": content}
        if artifacts:
            body["artifacts"] = artifacts
        return await self._request("POST", f"/dispatches/{dispatch_id}/complete", json=body)

    async def escalate(
        self,
        dispatch_id: str,
        reason: str = "",
        *,
        severity: str = "warning",
        context: dict | None = None,
    ) -> dict:
        body: dict[str, Any] = {"severity": severity, "reason": reason}
        if context:
            body["context"] = context
        return await self._request("POST", f"/dispatches/{dispatch_id}/escalate", json=body)

    async def cancel(self, dispatch_id: str, reason: str = "") -> dict:
        body: dict[str, Any] = {"reason": reason} if reason else {}
        return await self._request("POST", f"/dispatches/{dispatch_id}/cancel", json=body or None)

    # ── Pipelines ──

    async def list_my_pipelines(self) -> dict:
        return await self._request("GET", "/pipelines")

    # ── Stats / Audit ──

    async def get_stats(self, claw_id: str) -> dict:
        return await self._request("GET", f"/nodes/{claw_id}/stats")

    async def get_audit_log(
        self,
        *,
        pipeline_id: str | None = None,
        action: str | None = None,
        page: int = 1,
        page_size: int = 20,
    ) -> dict:
        params: dict[str, Any] = {"page": page, "pageSize": page_size}
        if pipeline_id:
            params["pipelineId"] = pipeline_id
        if action:
            params["action"] = action
        return await self._request("GET", "/audit-log", params=params)
