"""AgentRegistry — registry contract for agent discovery and lookup.

Phase 4 modular contract: AgentRegistry must support registration,
lookup by role, listing, and removal. Concrete implementations may
use in-memory dict, database, or external discovery service.
"""

from __future__ import annotations

from typing import Any, Protocol, runtime_checkable


@runtime_checkable
class Agent(Protocol):
    """Minimal agent interface for dispatch and security boundary.

    All agents registered in AgentRegistry must implement this protocol.
    """

    @property
    def role(self) -> str:
        """Agent role for dispatch and policy evaluation."""
        ...

    @property
    def identity(self) -> Any | None:
        """AgentIdentity for permission checks, or None if not secured."""
        ...

    @property
    def permission_gateway(self) -> Any | None:
        """PermissionGateway for security boundary, or None if not secured."""
        ...

    async def execute(self, payload: dict[str, Any]) -> Any:
        """Execute a task payload and return result."""
        ...

    def check_permission(self, action: str, resource: str | None = None) -> bool:
        """Check if agent has permission for action."""
        ...


@runtime_checkable
class AgentRegistry(Protocol):
    """Registry for agent discovery and lookup.

    All agents must be registered before they can be discovered by
    orchestrators, planners, or other agents.
    """

    def register(self, agent_id: str, agent: Agent) -> None:
        """Register an agent under a unique ID."""
        ...

    def unregister(self, agent_id: str) -> bool:
        """Remove an agent. Returns True if it existed."""
        ...

    def get(self, agent_id: str) -> Agent | None:
        """Retrieve an agent by ID, or None if not found."""
        ...

    def list_by_role(self, role: str) -> list[Agent]:
        """List all agents with a given role."""
        ...

    def list_all(self) -> list[tuple[str, Agent]]:
        """List all registered agents as (id, agent) pairs."""
        ...

    def exists(self, agent_id: str) -> bool:
        """Check whether an agent is registered."""
        ...


@runtime_checkable
class TaskDispatcher(Protocol):
    """Dispatcher for routing tasks to registered agents.

    Phase 4 modular contract: decouples task submission from agent
    execution. Concrete implementations may use direct dispatch,
    event-driven routing, or queue-based scheduling.

    All dispatches must pass through Identity → Policy → Permission → Audit
    before agent.execute() is called.
    """

    async def dispatch(self, task_id: str, agent_id: str, payload: dict[str, Any]) -> Any:
        """Dispatch a task to a specific agent and return the result."""
        ...

    async def dispatch_to_role(self, task_id: str, role: str, payload: dict[str, Any]) -> Any:
        """Dispatch a task to an available agent with a given role.

        Selection must be deterministic (e.g., round-robin or consistent hash).
        """
        ...

    def register_agent(self, agent_id: str, agent: Agent) -> None:
        """Register an agent for dispatching."""
        ...


__all__ = ["Agent", "AgentRegistry", "TaskDispatcher"]