"""
Agent handoff system for delegating specialized work while preserving context.

This module provides the handoff mechanism that enables agents to delegate
specialized work to other agents while maintaining full conversation history
and task context (AGENT-05).

Requirements: AGENT-05 (context preservation during handoff)
"""

from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field
from datetime import datetime, timezone

from gsd_rlm.execution.runner import AgentMessage, MultiAgentRuntime
from gsd_rlm.session.memory import FileSessionMemory, SessionState


@dataclass
class HandoffContext:
    """Full context for agent handoff (AGENT-05).

    Contains all information needed to delegate work to a specialist agent
    while preserving conversation history and task context.

    Attributes:
        source_agent: Agent initiating the handoff
        target_agent: Specialist agent receiving the handoff
        handoff_reason: Why delegation is happening
        conversation_history: Full conversation from session
        current_task: The task being delegated
        task_outputs: Outputs from previous tasks
        routing_chain: Agents that have processed this task so far
        metadata: Additional context
        timestamp: Auto-generated ISO timestamp
    """

    source_agent: str
    target_agent: str
    handoff_reason: str
    conversation_history: List[Dict[str, str]] = field(default_factory=list)
    current_task: str = ""
    task_outputs: List[Dict[str, Any]] = field(default_factory=list)
    routing_chain: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)
    timestamp: str = field(
        default_factory=lambda: datetime.now(timezone.utc).isoformat()
    )

    def to_agent_message(self) -> AgentMessage:
        """Convert to AgentMessage for execution.

        Creates an AgentMessage with:
        - task_id as "source->target" for traceability
        - Full conversation history preserved
        - Extended routing chain including target agent

        Returns:
            AgentMessage ready for execution through runtime
        """
        return AgentMessage(
            task_id=f"{self.source_agent}->{self.target_agent}",
            content=self.current_task,
            history=self.conversation_history,
            routing=self.routing_chain + [self.target_agent],
            metadata={
                "handoff_reason": self.handoff_reason,
                "source_agent": self.source_agent,
                **self.metadata,
            },
        )


class AgentHandoff:
    """Manage handoffs between agents with full context preservation (AGENT-05).

    This class handles the complete handoff lifecycle:
    1. Create handoff context from session
    2. Execute handoff through runtime
    3. Record handoff result in session

    Example:
        ```python
        from gsd_rlm.session.memory import FileSessionMemory
        from gsd_rlm.execution.handoff import AgentHandoff
        from pathlib import Path

        session_memory = FileSessionMemory(Path("sessions"))
        handoff = AgentHandoff(session_memory)

        # Create handoff context
        context = handoff.create_handoff(
            source_agent="generalist",
            target_agent="specialist",
            reason="Needs domain expertise",
            session_id="session-123",
            task="Analyze financial data"
        )

        # Execute handoff through runtime
        result = handoff.execute_handoff(context, runtime)

        # Record handoff in session
        handoff.record_handoff("session-123", context, result)
        ```
    """

    def __init__(self, session_memory: FileSessionMemory):
        """Initialize the handoff manager.

        Args:
            session_memory: Session persistence for context loading
        """
        self.session = session_memory

    def create_handoff(
        self,
        source_agent: str,
        target_agent: str,
        reason: str,
        session_id: str,
        task: str,
    ) -> HandoffContext:
        """Create a handoff context with full session context.

        Loads the session and extracts:
        - Full conversation history via get_context_for_llm()
        - Recent task outputs via get_recent_task_outputs()
        - Routing chain from previous task outputs

        Args:
            source_agent: Agent initiating the handoff
            target_agent: Specialist agent to receive the handoff
            reason: Why delegation is happening
            session_id: Session identifier for context loading
            task: The task being delegated

        Returns:
            HandoffContext with complete session context

        Raises:
            ValueError: If session does not exist
        """
        session = self.session.load(session_id)
        if not session:
            raise ValueError(f"Session not found: {session_id}")

        return HandoffContext(
            source_agent=source_agent,
            target_agent=target_agent,
            handoff_reason=reason,
            conversation_history=self.session.get_context_for_llm(session),
            current_task=task,
            task_outputs=self.session.get_recent_task_outputs(session),
            routing_chain=self._get_routing_chain(session),
        )

    def _get_routing_chain(self, session: SessionState) -> List[str]:
        """Extract routing chain from session task outputs.

        Builds a deduplicated list of agents that have processed
        this task by examining routing information from task outputs.

        Args:
            session: Session to extract routing from

        Returns:
            List of agent names in order of first appearance
        """
        chain = []
        for output in session.task_outputs:
            if output.routing:
                for agent in output.routing:
                    if agent not in chain:
                        chain.append(agent)
        return chain

    def execute_handoff(
        self,
        handoff_context: HandoffContext,
        runtime: MultiAgentRuntime,
    ) -> AgentMessage:
        """Execute handoff through the multi-agent runtime.

        Converts the handoff context to an AgentMessage and
        executes it through the provided runtime.

        Args:
            handoff_context: Context for the handoff
            runtime: Multi-agent runtime for execution

        Returns:
            AgentMessage result from execution
        """
        message = handoff_context.to_agent_message()
        return runtime.run(message)

    def record_handoff(
        self,
        session_id: str,
        handoff_context: HandoffContext,
        result: Any,
    ) -> None:
        """Record handoff in session for audit trail.

        Adds a message to the session documenting the handoff
        and saves the session.

        Args:
            session_id: Session to update
            handoff_context: Context that was used for handoff
            result: Result from the handoff execution
        """
        session = self.session.load(session_id)
        if not session:
            return  # Session was deleted, nothing to record

        # Create handoff record message
        from gsd_rlm.session.memory import SessionMessage

        result_summary = (
            str(result.content)[:500]
            if hasattr(result, "content")
            else str(result)[:500]
        )
        handoff_message = SessionMessage(
            role="system",
            content=f"Handoff: {handoff_context.source_agent} -> {handoff_context.target_agent}\n"
            f"Reason: {handoff_context.handoff_reason}\n"
            f"Task: {handoff_context.current_task}\n"
            f"Result: {result_summary}",
            timestamp=datetime.now(timezone.utc).isoformat(),
            metadata={
                "type": "handoff",
                "source_agent": handoff_context.source_agent,
                "target_agent": handoff_context.target_agent,
                "handoff_reason": handoff_context.handoff_reason,
            },
        )

        session.messages.append(handoff_message)
        self.session.save(session)
