"""Agent and policy registry for wl-apdp SDK.

The AgentRegistry is populated by the application at startup with
agent definitions, Cedar policies, and job-type-to-agent mappings.
The ApdpClient uses the registry to resolve agents and policies
without hard-coding any app-specific data.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any


@dataclass
class JobMapping:
    """Maps a job type to an agent and action.

    Attributes:
        agent_slug: The agent slug to invoke for this job type.
        action: The Cedar action ("read", "write", "execute").
        intent: Optional intent declaration for APDP authorization.
    """

    agent_slug: str
    action: str
    intent: dict[str, Any] | None = None


class AgentRegistry:
    """Registry of agents, policies, and job-type mappings.

    Applications populate the registry at startup, and the ApdpClient
    reads from it during authorization and initialization.
    """

    def __init__(self) -> None:
        self._agents: dict[str, dict[str, Any]] = {}
        self._policies: list[dict[str, Any]] = []
        self._job_mappings: dict[str, JobMapping] = {}

    def register_agent(self, definition: dict[str, Any]) -> None:
        """Register an agent definition.

        Args:
            definition: Agent dict with at least "slug" and "trust_level".

        Raises:
            ValueError: If "slug" is missing.
        """
        slug = definition.get("slug")
        if not slug:
            raise ValueError("Agent definition must include 'slug'")
        self._agents[slug] = definition

    def register_policy(self, policy: dict[str, Any]) -> None:
        """Register a Cedar policy definition.

        Args:
            policy: Policy dict with at least "id", "name", "code".

        Raises:
            ValueError: If "id" is missing.
        """
        if not policy.get("id"):
            raise ValueError("Policy definition must include 'id'")
        # Avoid duplicates by id
        existing_ids = {p["id"] for p in self._policies}
        if policy["id"] not in existing_ids:
            self._policies.append(policy)

    def register_job_type(self, job_type: str, mapping: JobMapping) -> None:
        """Register a job-type-to-agent mapping.

        Args:
            job_type: The job type identifier (e.g., "chat", "conflict-monitor").
            mapping: The JobMapping with agent_slug, action, and optional intent.
        """
        self._job_mappings[job_type] = mapping

    def get_agent(self, slug: str) -> dict[str, Any] | None:
        """Look up an agent definition by slug.

        Args:
            slug: The agent slug.

        Returns:
            The agent definition dict, or None if not found.
        """
        return self._agents.get(slug)

    def get_trust_level(self, slug: str) -> int:
        """Get the trust level for an agent.

        Args:
            slug: The agent slug.

        Returns:
            The trust level (0 if agent not found).
        """
        agent = self._agents.get(slug)
        return agent.get("trust_level", 0) if agent else 0

    def get_job_mapping(self, job_type: str) -> JobMapping | None:
        """Look up the job mapping for a job type.

        Args:
            job_type: The job type identifier.

        Returns:
            The JobMapping, or None if not registered.
        """
        return self._job_mappings.get(job_type)

    def all_agents(self) -> list[dict[str, Any]]:
        """Return all registered agent definitions."""
        return list(self._agents.values())

    def all_policies(self) -> list[dict[str, Any]]:
        """Return all registered Cedar policies."""
        return list(self._policies)

    def all_job_types(self) -> list[str]:
        """Return all registered job type identifiers."""
        return list(self._job_mappings.keys())
