"""
Agent Registry for Uncertainty Dispatch

WO-ECHO-UNCERTAINTY-DISPATCH: Extensible agent pool for handling queries
that require external information lookup.

When Echo detects uncertainty (knowledge gaps, temporal queries, codebase searches),
it dispatches specialized agents to gather information.

Usage:
    from helix_cdc.echo.agents import (
        AGENT_REGISTRY,
        register_agent,
        get_agents_for_uncertainty,
        DispatchableAgent,
        AgentResult,
    )

    # Register a custom agent
    register_agent(MyAgent())

    # Get agents for a query type
    agents = get_agents_for_uncertainty("codebase")
"""

import hashlib
import time
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional

# Project root for agent operations
PROJECT_ROOT = Path(__file__).resolve().parents[3]


def generate_agent_id() -> str:
    """Generate a unique agent execution ID."""
    timestamp = datetime.now(timezone.utc).isoformat()
    return hashlib.sha256(f"agent:{timestamp}:{time.time_ns()}".encode()).hexdigest()[:16]


@dataclass
class AgentResult:
    """
    Result from a single agent execution.

    Attributes:
        agent_id: Unique execution ID for this run
        agent_type: Type of agent that produced this result
        query: The query that was executed
        result: The actual result data (type depends on agent)
        sources: List of source references (file:line, URLs, etc.)
        confidence: Agent's confidence in the result (0-1)
        elapsed_ms: Execution time in milliseconds
        error: Error message if execution failed
    """
    agent_id: str
    agent_type: str
    query: str
    result: Any
    sources: List[str] = field(default_factory=list)
    confidence: float = 0.0
    elapsed_ms: float = 0.0
    error: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "agent_id": self.agent_id,
            "agent_type": self.agent_type,
            "query": self.query,
            "result": self.result,
            "sources": self.sources,
            "confidence": self.confidence,
            "elapsed_ms": self.elapsed_ms,
            "error": self.error,
        }


class DispatchableAgent(ABC):
    """
    Base class for dispatchable agents.

    Agents are specialized components that can be dispatched to gather
    information for uncertainty queries. Each agent has:
    - A unique type identifier
    - An async execute method
    - Optional initialization logic
    - Tool schema for LLM integration (WO-ECHO-AGENT-LOOP-01)
    """

    @property
    @abstractmethod
    def agent_type(self) -> str:
        """Unique identifier for this agent type."""
        ...

    @abstractmethod
    async def execute(self, query: str, context: Dict[str, Any]) -> AgentResult:
        """
        Execute the agent on a query.

        Args:
            query: The user's query to process
            context: Additional context (uncertainty_type, matched_keywords, etc.)

        Returns:
            AgentResult with the execution results
        """
        ...

    def available(self) -> bool:
        """
        Check if this agent is available/functional.

        Override to add availability checks (e.g., API keys, services).
        """
        return True

    def get_tool_schema(self) -> Dict[str, Any]:
        """
        Return OpenAI-compatible tool schema for LLM integration.

        WO-ECHO-AGENT-LOOP-01: Enables the agent loop to present this
        agent as a tool to the LLM.

        Override in subclasses to provide agent-specific parameters.

        Returns:
            Dict with 'type' and 'function' keys in OpenAI tool format
        """
        return {
            "type": "function",
            "function": {
                "name": self.agent_type,
                "description": self.__doc__ or f"{self.agent_type} agent",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "The query to execute"
                        }
                    },
                    "required": ["query"]
                }
            }
        }


# Global agent registry
# Maps agent_type -> DispatchableAgent instance
AGENT_REGISTRY: Dict[str, DispatchableAgent] = {}


def register_agent(agent: DispatchableAgent) -> None:
    """
    Register an agent in the global registry.

    Args:
        agent: The agent instance to register

    Raises:
        ValueError: If agent_type is already registered
    """
    if agent.agent_type in AGENT_REGISTRY:
        raise ValueError(f"Agent type '{agent.agent_type}' already registered")
    AGENT_REGISTRY[agent.agent_type] = agent


def get_agent(agent_type: str) -> Optional[DispatchableAgent]:
    """
    Get an agent by type.

    Args:
        agent_type: The agent type to look up

    Returns:
        The agent instance or None if not found
    """
    return AGENT_REGISTRY.get(agent_type)


def get_agents_for_uncertainty(uncertainty_type: str) -> List[str]:
    """
    Get list of agent types suitable for an uncertainty type.

    Maps uncertainty types to appropriate agents:
    - codebase: Search the helix-cdc codebase
    - temporal: Search for current/recent information
    - external: Search external documentation/APIs
    - general: Combination of available agents

    Args:
        uncertainty_type: The type of uncertainty detected

    Returns:
        List of agent type strings to dispatch
    """
    mapping = {
        "codebase": ["codebase"],
        "temporal": ["search"],
        "external": ["search", "retrieval"],
        "general": ["codebase", "search"],
        "execution": ["test"],  # WO-ECHO-TEST-AGENT-01: Test execution agent
        "modification": ["patch"],  # WO-ECHO-PATCH-AGENT-01: Patch/edit agent
        "planning": ["plan"],  # WO-ECHO-PLAN-AGENT-01: Plan/work order agent
        "conversation": ["chat"],  # WO-ECHO-CHAT-AGENT-01: LLM chat agent
    }

    requested = mapping.get(uncertainty_type, ["codebase"])

    # Filter to only registered + available agents
    available = [
        agent_type
        for agent_type in requested
        if agent_type in AGENT_REGISTRY and AGENT_REGISTRY[agent_type].available()
    ]

    return available if available else ["codebase"]


def list_agents() -> List[Dict[str, Any]]:
    """
    List all registered agents with their status.

    Returns:
        List of dicts with agent_type, available, and class_name
    """
    return [
        {
            "agent_type": agent.agent_type,
            "available": agent.available(),
            "class_name": agent.__class__.__name__,
        }
        for agent in AGENT_REGISTRY.values()
    ]


# Export public API
__all__ = [
    "DispatchableAgent",
    "AgentResult",
    "AGENT_REGISTRY",
    "register_agent",
    "get_agent",
    "get_agents_for_uncertainty",
    "list_agents",
    "generate_agent_id",
    "PROJECT_ROOT",
]
