from __future__ import annotations

from typing import Any
from uuid import UUID

import httpx
from fastapi import HTTPException

from security.auth.verifier import build_internal_auth_headers


def _merge_headers(extra: dict[str, str] | None) -> dict[str, str]:
    h = build_internal_auth_headers()
    if extra:
        h = {**h, **extra}
    return h


async def _json_or_empty(response: httpx.Response) -> Any:
    if response.status_code == 204:
        return None
    try:
        response.raise_for_status()
    except httpx.HTTPStatusError as exc:
        detail: Any = exc.response.text
        try:
            detail = exc.response.json()
        except Exception:
            pass
        raise HTTPException(status_code=exc.response.status_code, detail=detail) from exc
    if not response.content:
        return None
    return response.json()


class NormbaseClient:
    def __init__(self, client: httpx.AsyncClient, base_url: str) -> None:
        self._client = client
        self._base = base_url.rstrip("/")

    async def list_fields(self, *, status: str | None = None) -> Any:
        params = {}
        if status is not None:
            params["status"] = status
        r = await self._client.get(
            f"{self._base}/api/v1/fields",
            headers=_merge_headers(None),
            params=params or None,
        )
        return await _json_or_empty(r)

    async def create_field(self, body: dict[str, Any]) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/fields",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def patch_field(self, field_id: UUID, body: dict[str, Any]) -> Any:
        r = await self._client.patch(
            f"{self._base}/api/v1/fields/{field_id}",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def delete_field(self, field_id: UUID) -> Any:
        r = await self._client.delete(
            f"{self._base}/api/v1/fields/{field_id}",
            headers=_merge_headers(None),
        )
        return await _json_or_empty(r)

    async def list_norms(self, params: dict[str, Any]) -> Any:
        r = await self._client.get(
            f"{self._base}/api/v1/norms",
            headers=_merge_headers(None),
            params=params,
        )
        return await _json_or_empty(r)

    async def get_norm(self, norm_id: UUID) -> Any:
        r = await self._client.get(
            f"{self._base}/api/v1/norms/{norm_id}",
            headers=_merge_headers(None),
        )
        return await _json_or_empty(r)

    async def create_norm(self, body: dict[str, Any]) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/norms",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def update_norm(self, norm_id: UUID, body: dict[str, Any]) -> Any:
        r = await self._client.put(
            f"{self._base}/api/v1/norms/{norm_id}",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def delete_norm(self, norm_id: UUID) -> None:
        r = await self._client.delete(
            f"{self._base}/api/v1/norms/{norm_id}",
            headers=_merge_headers(None),
        )
        await _json_or_empty(r)

    async def issue_key(self, body: dict[str, Any]) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/keys/issue",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def issue_field_key(self, body: dict[str, Any]) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/keys/field/issue",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def list_keys(self, params: dict[str, Any]) -> Any:
        r = await self._client.get(
            f"{self._base}/api/v1/keys",
            headers=_merge_headers(None),
            params=params,
        )
        return await _json_or_empty(r)

    async def revoke_key(self, key_id: UUID) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/keys/{key_id}/revoke",
            headers=_merge_headers(None),
        )
        return await _json_or_empty(r)

    async def rotate_key(self, key_id: UUID) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/keys/{key_id}/rotate",
            headers=_merge_headers(None),
        )
        return await _json_or_empty(r)

    async def health_head(self) -> bool:
        try:
            r = await self._client.get(f"{self._base}/health", headers=_merge_headers(None))
            return r.status_code < 500
        except Exception:
            return False


class EdaClient:
    def __init__(self, client: httpx.AsyncClient, base_url: str) -> None:
        self._client = client
        self._base = base_url.rstrip("/")

    async def list_agents(self, *, limit: int = 100) -> Any:
        r = await self._client.get(
            f"{self._base}/api/v1/agents",
            headers=_merge_headers(None),
            params={"limit": limit},
        )
        return await _json_or_empty(r)

    async def create_agent(self, body: dict[str, Any]) -> Any:
        r = await self._client.post(
            f"{self._base}/api/v1/agents",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def get_agent(self, agent_id: UUID) -> Any:
        r = await self._client.get(
            f"{self._base}/api/v1/agents/{agent_id}",
            headers=_merge_headers(None),
        )
        return await _json_or_empty(r)

    async def patch_agent(self, agent_id: UUID, body: dict[str, Any]) -> Any:
        r = await self._client.patch(
            f"{self._base}/api/v1/agents/{agent_id}",
            headers=_merge_headers(None),
            json=body,
        )
        return await _json_or_empty(r)

    async def delete_agent(self, agent_id: UUID) -> None:
        r = await self._client.delete(
            f"{self._base}/api/v1/agents/{agent_id}",
            headers=_merge_headers(None),
        )
        await _json_or_empty(r)

    async def health_head(self) -> bool:
        try:
            r = await self._client.get(f"{self._base}/health", headers=_merge_headers(None))
            return r.status_code < 500
        except Exception:
            return False
