# Copyright (c) Microsoft. All rights reserved.

import asyncio
from typing import Any

from agent_framework import Agent, AgentResponseUpdate, WorkflowEvent
from dotenv import load_dotenv

"""AutoGen Swarm pattern vs Agent Framework HandoffBuilder.

Demonstrates agent handoff coordination where agents can transfer control
to other specialized agents based on the task requirements.
"""

# Load environment variables from .env file
load_dotenv()


async def run_autogen() -> None:
    """AutoGen's Swarm pattern with human-in-the-loop handoffs."""

    from autogen_agentchat.agents import AssistantAgent
    from autogen_agentchat.conditions import HandoffTermination, TextMentionTermination
    from autogen_agentchat.messages import HandoffMessage
    from autogen_agentchat.teams import Swarm
    from autogen_agentchat.ui import Console
    from autogen_ext.models.openai import OpenAIChatCompletionClient

    client = OpenAIChatCompletionClient(model="gpt-4.1-mini")

    # Create triage agent that routes to specialists
    triage_agent = AssistantAgent(
        name="triage",
        model_client=client,
        system_message=(
            "You are a triage agent. Analyze the user's request and hand off to the appropriate specialist.\n"
            "If you need information from the user, first send your message, then handoff to user.\n"
            "Use TERMINATE when the issue is fully resolved."
        ),
        handoffs=["billing_agent", "technical_support", "user"],
        model_client_stream=True,
    )

    # Create billing specialist
    billing_agent = AssistantAgent(
        name="billing_agent",
        model_client=client,
        system_message=(
            "You are a billing specialist. Help with payment and billing questions.\n"
            "If you need information from the user, first send your message, then handoff to user.\n"
            "When the issue is resolved, handoff to triage to finalize."
        ),
        handoffs=["triage", "user"],
        model_client_stream=True,
    )

    # Create technical support specialist
    tech_support = AssistantAgent(
        name="technical_support",
        model_client=client,
        system_message=(
            "You are technical support. Help with technical issues.\n"
            "If you need information from the user, first send your message, then handoff to user.\n"
            "When the issue is resolved, handoff to triage to finalize."
        ),
        handoffs=["triage", "user"],
        model_client_stream=True,
    )

    # Create swarm team with human-in-the-loop termination
    termination = HandoffTermination(target="user") | TextMentionTermination("TERMINATE")
    team = Swarm(
        participants=[triage_agent, billing_agent, tech_support],
        termination_condition=termination,
    )

    # Scripted user responses for demonstration
    scripted_responses = [
        "I was charged twice for my subscription",
        "Yes, the charge of $49.99 appears twice on my credit card statement.",
        "Thank you for your help!",
    ]
    response_index = 0

    # Run with human-in-the-loop pattern
    print("[AutoGen] Swarm handoff conversation:")
    task_result = await Console(team.run_stream(task=scripted_responses[response_index]))
    last_message = task_result.messages[-1]
    response_index += 1

    # Continue conversation when agents handoff to user
    while (
        isinstance(last_message, HandoffMessage)
        and last_message.target == "user"
        and response_index < len(scripted_responses)
    ):
        user_message = scripted_responses[response_index]
        task_result = await Console(
            team.run_stream(task=HandoffMessage(source="user", target=last_message.source, content=user_message))
        )
        last_message = task_result.messages[-1]
        response_index += 1


async def run_agent_framework() -> None:
    """Agent Framework's HandoffBuilder for agent coordination."""
    from agent_framework import (
        WorkflowRunState,
    )
    from agent_framework.openai import OpenAIChatClient
    from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder

    client = OpenAIChatClient(model="gpt-4.1-mini")

    # Create triage agent
    triage_agent = Agent(client=client,
        name="triage",
        instructions=(
            "You are a triage agent. Analyze the user's request and route to the appropriate specialist:\n"
            "- For billing issues: call handoff_to_billing_agent\n"
            "- For technical issues: call handoff_to_technical_support"
        ),
        description="Routes requests to appropriate specialists",
        require_per_service_call_history_persistence=True,
    )

    # Create billing specialist
    billing_agent = Agent(client=client,
        name="billing_agent",
        instructions="You are a billing specialist. Help with payment and billing questions. Provide clear assistance.",
        description="Handles billing and payment questions",
        require_per_service_call_history_persistence=True,
    )

    # Create technical support specialist
    tech_support = Agent(client=client,
        name="technical_support",
        instructions="You are technical support. Help with technical issues. Provide clear assistance.",
        description="Handles technical support questions",
        require_per_service_call_history_persistence=True,
    )

    # Create handoff workflow - simpler configuration
    # After specialists respond, control returns to user (via triage as coordinator)
    workflow = (
        HandoffBuilder(
            name="support_handoff",
            participants=[triage_agent, billing_agent, tech_support],
            termination_condition=lambda conv: sum(1 for msg in conv if msg.role == "user") > 3,
        )
        .with_start_agent(triage_agent)
        .add_handoff(triage_agent, [billing_agent, tech_support])
        .build()
    )

    # Scripted user responses
    scripted_responses = [
        "I was charged twice for my subscription",
        "Yes, the charge of $49.99 appears twice on my credit card statement.",
        "Thank you for your help!",
    ]

    # Run with initial message
    print("[Agent Framework] Handoff conversation:")
    print("---------- user ----------")
    print(scripted_responses[0])

    current_executor = None
    stream_line_open = False
    pending_requests: list[WorkflowEvent] = []

    async for event in workflow.run(scripted_responses[0], stream=True):
        if event.type == "output" and isinstance(event.data, AgentResponseUpdate):
            # Print executor name header when switching to a new agent
            if current_executor != event.executor_id:
                if stream_line_open:
                    print()
                    stream_line_open = False
                print(f"---------- {event.executor_id} ----------")
                current_executor = event.executor_id
                stream_line_open = True
            if event.data:
                print(event.data.text, end="", flush=True)
        elif event.type == "request_info":
            if isinstance(event.data, HandoffAgentUserRequest):
                pending_requests.append(event)
        elif event.type == "status":
            if event.state in {WorkflowRunState.IDLE_WITH_PENDING_REQUESTS} and stream_line_open:
                print()
                stream_line_open = False

    # Process scripted responses
    response_index = 1
    while pending_requests and response_index < len(scripted_responses):
        user_response = scripted_responses[response_index]
        print("---------- user ----------")
        print(user_response)

        responses: dict[str, Any] = {
            req.request_id: HandoffAgentUserRequest.create_response(user_response) for req in pending_requests
        }  # type: ignore
        pending_requests = []
        current_executor = None
        stream_line_open = False

        async for event in workflow.run(stream=True, responses=responses):
            if event.type == "output" and isinstance(event.data, AgentResponseUpdate):
                # Print executor name header when switching to a new agent
                if current_executor != event.executor_id:
                    if stream_line_open:
                        print()
                        stream_line_open = False
                    print(f"---------- {event.executor_id} ----------")
                    current_executor = event.executor_id
                    stream_line_open = True
                if event.data:
                    print(event.data.text, end="", flush=True)
            elif event.type == "request_info":
                if isinstance(event.data, HandoffAgentUserRequest):
                    pending_requests.append(event)
            elif event.type == "status":
                if (
                    event.state in {WorkflowRunState.IDLE_WITH_PENDING_REQUESTS, WorkflowRunState.IDLE}
                    and stream_line_open
                ):
                    print()
                    stream_line_open = False

        response_index += 1

    if stream_line_open:
        print()
    print()  # Final newline after conversation


async def main() -> None:
    print("=" * 60)
    print("Swarm / Handoff Pattern Comparison")
    print("=" * 60)
    print("AutoGen: Swarm with handoffs")
    print("Agent Framework: HandoffBuilder\n")
    await run_autogen()
    print()
    await run_agent_framework()


if __name__ == "__main__":
    asyncio.run(main())
