"""HTTP clients for Normbase and EDA services."""
from __future__ import annotations

import os
import uuid
from typing import Any, Dict, List, Literal

import httpx

from security.auth.verifier import build_internal_auth_headers


def _normbase_base_url() -> str:
    return os.getenv("NORMBASE_BASE_URL", "http://localhost:8001").rstrip("/")


def _eda_base_url() -> str:
    return os.getenv("EDA_BASE_URL", "http://localhost:8002").rstrip("/")


def _build_runtime_auth_headers(caller_api_key: str = "") -> dict[str, str]:
    key = (caller_api_key or "").strip()
    if not key:
        raise PermissionError("Unauthorized: missing MCP caller API key.")
    return {"X-API-Key": key}


def _normalize_agent_uuid(agent_id: str) -> str:
    text = (agent_id or "").strip()
    if not text:
        return ""
    return str(uuid.UUID(text))


async def retrieve_norms(
    context: Dict[str, Any],
    query_mode: Literal["structured", "semantic", "hybrid"] = "hybrid",
    nl_query: str | None = None,
    agent_role: str | None = None,
    agent_ids: List[str] | None = None,
    tag_filters: Dict[str, List[str]] | None = None,
    top_k: int = 20,
    field_ids: List[str] | None = None,
    request_id: str = "",
    caller_id: str = "",
    agent_id: str = "",
    key_id: str = "",
    caller_api_key: str = "",
) -> Dict[str, Any]:
    payload = {
        "context": context,
        "query_mode": query_mode,
        "top_k": top_k,
    }
    if nl_query:
        payload["nl_query"] = nl_query
    if agent_role:
        payload["agent_role"] = agent_role
    if agent_ids:
        payload["agent_ids"] = agent_ids
    if tag_filters:
        payload["tag_filters"] = tag_filters
    if field_ids:
        payload["field_ids"] = field_ids
    headers = _build_runtime_auth_headers(caller_api_key)
    if request_id:
        headers["X-Request-Id"] = request_id
    if caller_id:
        headers["X-Caller-Id"] = caller_id
    if agent_id:
        headers["X-Agent-Id"] = agent_id
    if key_id:
        headers["X-Key-Id"] = key_id
    async with httpx.AsyncClient(timeout=45.0) as client:
        resp = await client.post(
            f"{_normbase_base_url()}/api/v1/retrieve",
            json=payload,
            headers=headers,
        )
        resp.raise_for_status()
        return resp.json()


async def get_agent_field_ids(agent_id: str) -> List[str]:
    """Fetch EDA active_fields and keep only UUID-like field IDs."""
    normalized_agent_id = _normalize_agent_uuid(agent_id)
    if not normalized_agent_id:
        return []
    headers = build_internal_auth_headers()
    async with httpx.AsyncClient(timeout=20.0) as client:
        resp = await client.get(
            f"{_eda_base_url()}/api/v1/agents/{normalized_agent_id}",
            headers=headers,
        )
        resp.raise_for_status()
        data = resp.json() or {}
    raw_fields = data.get("active_fields") or []
    field_ids: List[str] = []
    for value in raw_fields:
        text = str(value).strip()
        if not text:
            continue
        try:
            field_ids.append(str(uuid.UUID(text)))
        except ValueError:
            continue
    return field_ids


async def plan_retrieval(
    context: Dict[str, Any],
    field_ids: List[str] | None = None,
    top_k: int = 5,
    request_id: str = "",
    caller_id: str = "",
    agent_id: str = "",
    key_id: str = "",
    caller_api_key: str = "",
) -> Dict[str, Any]:
    """Call Normbase retrieval planning endpoint."""
    payload: Dict[str, Any] = {
        "context": context,
        "top_k": top_k,
    }
    if field_ids:
        payload["field_ids"] = field_ids
    headers = _build_runtime_auth_headers(caller_api_key)
    if request_id:
        headers["X-Request-Id"] = request_id
    if caller_id:
        headers["X-Caller-Id"] = caller_id
    if agent_id:
        headers["X-Agent-Id"] = agent_id
    if key_id:
        headers["X-Key-Id"] = key_id
    async with httpx.AsyncClient(timeout=30.0) as client:
        resp = await client.post(
            f"{_normbase_base_url()}/api/v1/roles/plan",
            json=payload,
            headers=headers,
        )
        resp.raise_for_status()
        return resp.json()


async def get_traces(
    agent_id: str,
    limit: int = 20,
    request_id: str = "",
    caller_id: str = "",
    key_id: str = "",
) -> List[Dict[str, Any]]:
    headers = build_internal_auth_headers()
    if request_id:
        headers["X-Request-Id"] = request_id
    if caller_id:
        headers["X-Caller-Id"] = caller_id
    normalized_agent_id = _normalize_agent_uuid(agent_id)
    if normalized_agent_id:
        headers["X-Agent-Id"] = normalized_agent_id
    if key_id:
        headers["X-Key-Id"] = key_id
    async with httpx.AsyncClient(timeout=30.0) as client:
        resp = await client.get(
            f"{_eda_base_url()}/api/v1/agents/{normalized_agent_id}/traces",
            params={"limit": limit},
            headers=headers,
        )
        resp.raise_for_status()
        return resp.json()


async def check_action_compliance(
    usage_token: str,
    action_description: str,
    norm_ids: List[str] | None = None,
    request_id: str = "",
    caller_id: str = "",
    agent_id: str = "",
    key_id: str = "",
    caller_api_key: str = "",
) -> Dict[str, Any]:
    payload: Dict[str, Any] = {
        "usage_token": usage_token,
        "action_description": action_description,
    }
    if norm_ids:
        payload["norm_ids"] = norm_ids
    headers = _build_runtime_auth_headers(caller_api_key)
    if request_id:
        headers["X-Request-Id"] = request_id
    if caller_id:
        headers["X-Caller-Id"] = caller_id
    if agent_id:
        headers["X-Agent-Id"] = agent_id
    if key_id:
        headers["X-Key-Id"] = key_id
    async with httpx.AsyncClient(timeout=30.0) as client:
        resp = await client.post(
            f"{_normbase_base_url()}/api/v1/norms/check_action_compliance",
            json=payload,
            headers=headers,
        )
        resp.raise_for_status()
        return resp.json()


async def report_norm_outcome(
    usage_token: str,
    norm_id: str,
    outcome: str,
    details: Dict[str, Any] | None = None,
    request_id: str = "",
    caller_id: str = "",
    agent_id: str = "",
    key_id: str = "",
    caller_api_key: str = "",
) -> Dict[str, Any]:
    payload: Dict[str, Any] = {
        "usage_token": usage_token,
        "norm_id": norm_id,
        "outcome": outcome,
        "details": details or {},
    }
    if agent_id:
        payload["agent_id"] = agent_id
    headers = _build_runtime_auth_headers(caller_api_key)
    if request_id:
        headers["X-Request-Id"] = request_id
    if caller_id:
        headers["X-Caller-Id"] = caller_id
    if agent_id:
        headers["X-Agent-Id"] = agent_id
    if key_id:
        headers["X-Key-Id"] = key_id
    async with httpx.AsyncClient(timeout=30.0) as client:
        resp = await client.post(
            f"{_normbase_base_url()}/api/v1/norms/log-outcome",
            json=payload,
            headers=headers,
        )
        resp.raise_for_status()
        return resp.json()

