"""HTTP clients for dashboard aggregation and proxy calls."""
from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import httpx

from dashboard.config import settings
from security.auth.verifier import build_internal_auth_headers


class DashboardClients:
    def __init__(self):
        self._headers = build_internal_auth_headers()

    async def _get(self, url: str, params: dict[str, Any] | None = None) -> Any:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.get(url, params=params, headers=self._headers)
            resp.raise_for_status()
            return resp.json()

    async def _post(self, url: str, payload: dict[str, Any]) -> Any:
        async with httpx.AsyncClient(timeout=60.0) as client:
            resp = await client.post(url, json=payload, headers=self._headers)
            resp.raise_for_status()
            return resp.json()

    async def _put(self, url: str, payload: dict[str, Any]) -> Any:
        async with httpx.AsyncClient(timeout=60.0) as client:
            resp = await client.put(url, json=payload, headers=self._headers)
            resp.raise_for_status()
            return resp.json()

    async def _patch(self, url: str, payload: dict[str, Any]) -> Any:
        async with httpx.AsyncClient(timeout=60.0) as client:
            resp = await client.patch(url, json=payload, headers=self._headers)
            resp.raise_for_status()
            return resp.json()

    async def _delete(self, url: str) -> Any:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.delete(url, headers=self._headers)
            if resp.status_code == 204:
                return {"ok": True}
            resp.raise_for_status()
            return resp.json() if resp.text else {"ok": True}

    async def normbase_health(self) -> Any:
        return await self._get(f"{settings.normbase_base_url}/health")

    async def eda_health(self) -> Any:
        return await self._get(f"{settings.eda_base_url}/health")

    async def mcp_health(self) -> dict[str, Any]:
        async with httpx.AsyncClient(timeout=10.0) as client:
            # Basic reachability probe for streamable-http endpoint
            resp = await client.get(f"{settings.mcp_base_url}/mcp")
            return {"status_code": resp.status_code, "reachable": resp.status_code in (200, 406)}

    async def mcp_rpc(self, payload: dict[str, Any]) -> Any:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.post(f"{settings.mcp_base_url}/mcp", json=payload)
            resp.raise_for_status()
            return resp.json()

    async def list_norms(self, page_size: int = 100) -> Any:
        return await self._get(f"{settings.normbase_base_url}/api/v1/norms", params={"page_size": page_size})

    async def get_norm(self, norm_id: str) -> Any:
        return await self._get(f"{settings.normbase_base_url}/api/v1/norms/{norm_id}")

    async def create_norm(self, payload: dict[str, Any]) -> Any:
        return await self._post(f"{settings.normbase_base_url}/api/v1/norms", payload)

    async def update_norm(self, norm_id: str, payload: dict[str, Any]) -> Any:
        return await self._put(f"{settings.normbase_base_url}/api/v1/norms/{norm_id}", payload)

    async def delete_norm(self, norm_id: str) -> Any:
        return await self._delete(f"{settings.normbase_base_url}/api/v1/norms/{norm_id}")

    async def retrieve_norms(self, payload: dict[str, Any]) -> Any:
        return await self._post(f"{settings.normbase_base_url}/api/v1/retrieve", payload)

    async def list_agents(self, limit: int = 100) -> Any:
        return await self._get(f"{settings.eda_base_url}/api/v1/agents", params={"limit": limit})

    async def create_agent(self, payload: dict[str, Any]) -> Any:
        return await self._post(f"{settings.eda_base_url}/api/v1/agents", payload)

    async def get_agent(self, agent_id: str) -> Any:
        return await self._get(f"{settings.eda_base_url}/api/v1/agents/{agent_id}")

    async def update_agent(self, agent_id: str, payload: dict[str, Any]) -> Any:
        return await self._patch(f"{settings.eda_base_url}/api/v1/agents/{agent_id}", payload)

    async def delete_agent(self, agent_id: str) -> Any:
        return await self._delete(f"{settings.eda_base_url}/api/v1/agents/{agent_id}")

    async def agent_action(self, agent_id: str, action: str, payload: dict[str, Any]) -> Any:
        return await self._post(f"{settings.eda_base_url}/api/v1/agents/{agent_id}/{action}", payload)

    async def get_traces(self, agent_id: str, limit: int = 20) -> Any:
        return await self._get(
            f"{settings.eda_base_url}/api/v1/agents/{agent_id}/traces",
            params={"limit": limit},
        )

    async def issue_key(self, payload: dict[str, Any]) -> Any:
        return await self._post(f"{settings.normbase_base_url}/api/v1/keys/issue", payload)

    async def list_keys(self, agent_id: str | None = None, include_inactive: bool = False) -> Any:
        params: dict[str, Any] = {"include_inactive": include_inactive}
        if agent_id:
            params["agent_id"] = agent_id
        return await self._get(f"{settings.normbase_base_url}/api/v1/keys", params=params)

    async def revoke_key(self, key_id: str) -> Any:
        return await self._post(f"{settings.normbase_base_url}/api/v1/keys/{key_id}/revoke", {})

    async def rotate_key(self, key_id: str) -> Any:
        return await self._post(f"{settings.normbase_base_url}/api/v1/keys/{key_id}/rotate", {})

    def read_mcp_audit(self, limit: int = 100) -> list[dict[str, Any]]:
        if not settings.mcp_audit_log:
            return []
        path = Path(settings.mcp_audit_log)
        if not path.exists():
            return []
        lines = path.read_text(encoding="utf-8").splitlines()[-limit:]
        rows: list[dict[str, Any]] = []
        for line in lines:
            try:
                rows.append(json.loads(line))
            except Exception:
                continue
        return rows

