"""
ATFClient — ATF (Agent Trust Framework) trust-level client over NATS.

Sends trust check requests to the mesh ATF service via NATS and returns
the numeric trust level (0–4) for a given agent DID.

Trust levels (matching the TypeScript ATF_LEVELS enum):

+-------+----------+--------------------------------------------------+
| Level | Name     | Meaning                                          |
+-------+----------+--------------------------------------------------+
| 0     | INTERN   | No trust — read-only, supervised                 |
| 1     | JUNIOR   | Limited write access                             |
| 2     | SENIOR   | Full operational access                          |
| 3     | PRINCIPAL| Cross-mesh authority                             |
| 4     | SYSTEM   | Root — infrastructure-level operations only      |
+-------+----------+--------------------------------------------------+

Example::

    async with MeshNode() as mesh:
        level = await mesh.atf.get_trust_level("did:mesh:my-agent")
        if level < ATFLevel.SENIOR:
            raise PermissionError("Insufficient trust level")
"""

from __future__ import annotations

import json
import uuid
from enum import IntEnum

from nats.aio.client import Client


class ATFLevel(IntEnum):
    """ATF trust levels, matching the TypeScript ``ATF_LEVELS`` enum.

    Use these constants instead of raw integers for readability and
    forward-compatibility.
    """

    INTERN = 0
    JUNIOR = 1
    SENIOR = 2
    PRINCIPAL = 3
    SYSTEM = 4


class ATFError(Exception):
    """Raised when the mesh ATF service returns an error response."""

    def __init__(self, message: str, agent_id: str | None = None) -> None:
        super().__init__(message)
        self.agent_id = agent_id


# NATS subject for ATF trust check requests (must match TypeScript bridge config)
_ATF_TRUST_CHECK_SUBJECT = "mesh.atf.trust.check"


class ATFClient:
    """ATF trust-level client for the mesh Agent Trust Framework.

    Communicates with the TypeScript ATF service over NATS.

    Args:
        nc: Connected NATS client.
        timeout: Request timeout in seconds. Defaults to 5.
    """

    def __init__(self, nc: Client, timeout: float = 5.0) -> None:
        self._nc = nc
        self._timeout = timeout

    async def get_trust_level(self, agent_id: str) -> ATFLevel:
        """Query the trust level for a given agent DID.

        Sends a trust check request to the ATF service and returns the
        current trust level for the agent.

        Args:
            agent_id: Agent DID (e.g. ``"did:mesh:my-agent"``). Must be non-empty.

        Returns:
            The agent's current ``ATFLevel``.

        Raises:
            ValueError: If ``agent_id`` is empty.
            ATFError: If the ATF service returns an error or an unknown level.
            nats.errors.TimeoutError: If the request times out.
        """
        if not agent_id or not agent_id.strip():
            raise ValueError("atf.get_trust_level: agent_id must be non-empty")

        request_id = str(uuid.uuid4())
        payload = json.dumps({"id": request_id, "agentId": agent_id}).encode("utf-8")
        response = await self._nc.request(_ATF_TRUST_CHECK_SUBJECT, payload, timeout=self._timeout)

        try:
            result = json.loads(response.data.decode("utf-8"))
        except (json.JSONDecodeError, UnicodeDecodeError) as exc:
            raise ATFError(f"Failed to decode ATF response: {exc}", agent_id=agent_id) from exc

        if "error" in result:
            raise ATFError(str(result["error"]), agent_id=agent_id)

        raw_level = result.get("trustLevel")
        if raw_level is None:
            raise ATFError(
                f"ATF response missing 'trustLevel' field for agent {agent_id!r}",
                agent_id=agent_id,
            )

        try:
            return ATFLevel(int(raw_level))
        except ValueError as exc:
            raise ATFError(
                f"Unknown ATF trust level {raw_level!r} for agent {agent_id!r}",
                agent_id=agent_id,
            ) from exc

    async def check_minimum(self, agent_id: str, minimum: ATFLevel) -> bool:
        """Check whether an agent meets a minimum trust level.

        Convenience wrapper around ``get_trust_level`` that returns a boolean
        instead of raising an exception on insufficient trust.

        Args:
            agent_id: Agent DID.
            minimum: Minimum required ``ATFLevel``.

        Returns:
            ``True`` if the agent's trust level >= ``minimum``, ``False`` otherwise.

        Raises:
            ValueError: If ``agent_id`` is empty.
            ATFError: If the ATF service returns an error.
        """
        level = await self.get_trust_level(agent_id)
        return level >= minimum
