"""
Handoff — control transfer between agents.

Design notes:
  - Three modes: delegate (call & wait), transfer (full handover), broadcast (fan-out)
  - Conditional handoffs: only trigger when a predicate is met
  - Handoffs carry context (messages, state snapshot) to the target agent
  - Inspired by OpenAI Swarm's simplicity but with production-grade features
"""

from __future__ import annotations

from collections.abc import Callable
from datetime import datetime
from enum import StrEnum
from typing import Any

from pydantic import BaseModel, Field

from nexus.core.message import Message
from nexus.types import new_id


class HandoffMode(StrEnum):
    """
    The three modes of control transfer between agents.

    DELEGATE:   Source agent sends a sub-task and waits for the result.
                The source agent retains control and can post-process the result.
                (like calling a function)

    TRANSFER:   Source agent fully hands over control to the target.
                The source agent is done — the target takes over the conversation.
                (like a customer service transfer)

    BROADCAST:  Source agent sends the same task to multiple agents.
                Results are collected and the best one wins (or they're aggregated).
                (like a competitive bid / consensus vote)
    """

    DELEGATE = "delegate"
    TRANSFER = "transfer"
    BROADCAST = "broadcast"


class Handoff(BaseModel):
    """
    Describes a control transfer from one agent to another.

    Can be created manually or returned by an agent's tool function
    (Swarm-style) to trigger automatic routing.
    """

    id: str = Field(default_factory=new_id)
    mode: HandoffMode = HandoffMode.DELEGATE

    # Routing
    source_agent_id: str | None = None
    target_agent_id: str | None = None
    target_agent_ids: list[str] = Field(default_factory=list)  # For BROADCAST mode

    # Context passed to the target
    message: str = ""
    messages: list[Message] = Field(default_factory=list)
    context_data: dict[str, Any] = Field(default_factory=dict)

    # Conditional handoff
    condition: str | None = Field(
        default=None,
        description="Natural language condition for when this handoff should trigger",
    )

    # Execution result (filled after completion)
    result: str | None = None
    results: dict[str, str] = Field(default_factory=dict)  # agent_id → result for BROADCAST
    status: str = "pending"  # pending, in_progress, completed, failed

    # Timing
    created_at: datetime = Field(default_factory=datetime.utcnow)
    completed_at: datetime | None = None

    metadata: dict[str, Any] = Field(default_factory=dict)

    def complete(self, result: str) -> None:
        """Mark the handoff as completed."""
        self.result = result
        self.status = "completed"
        self.completed_at = datetime.utcnow()

    def fail(self, error: str) -> None:
        """Mark the handoff as failed."""
        self.result = error
        self.status = "failed"
        self.completed_at = datetime.utcnow()

    @property
    def is_broadcast(self) -> bool:
        return self.mode == HandoffMode.BROADCAST

    @property
    def is_done(self) -> bool:
        return self.status in ("completed", "failed")


# ---------------------------------------------------------------------------
# Handoff builder helpers (fluent API)
# ---------------------------------------------------------------------------


def delegate_to(agent_id: str, message: str = "", **context: Any) -> Handoff:
    """Create a DELEGATE handoff — call an agent and wait for the result."""
    return Handoff(
        mode=HandoffMode.DELEGATE,
        target_agent_id=agent_id,
        message=message,
        context_data=context,
    )


def transfer_to(agent_id: str, message: str = "", **context: Any) -> Handoff:
    """Create a TRANSFER handoff — fully hand over to another agent."""
    return Handoff(
        mode=HandoffMode.TRANSFER,
        target_agent_id=agent_id,
        message=message,
        context_data=context,
    )


def broadcast_to(
    agent_ids: list[str],
    message: str = "",
    **context: Any,
) -> Handoff:
    """Create a BROADCAST handoff — fan out to multiple agents."""
    return Handoff(
        mode=HandoffMode.BROADCAST,
        target_agent_ids=agent_ids,
        message=message,
        context_data=context,
    )


# ---------------------------------------------------------------------------
# Handoff function type (for Swarm-style tool returns)
# ---------------------------------------------------------------------------

# An agent tool can return a HandoffRequest to trigger a handoff
HandoffFunction = Callable[..., Handoff]
