"""
08_debate_agents.py — Multi-round debate between two agents + judge

Tests repeated agent calls within a loop. Two agents argue opposite sides
across 2 rounds, then a judge delivers a verdict. The graph shows multiple
instances of each agent (one node per call = one node per round).

Pipeline (2 rounds):
  moderator
    Round 1: proponent_r1 ←→ opponent_r1
    Round 2: proponent_r2 ←→ opponent_r2
  → judge

WHAT THE DASHBOARD SHOULD SHOW:
  Live Graph:
    • moderator → proponent_r1 and opponent_r1 (parallel, round 1)
    • proponent_r2 and opponent_r2 after round 1 (sequential, each referencing prior)
    • judge as final merge node
    • Multiple instances of "proponent" / "opponent" visible as separate nodes
      (because agent_id is unique per call, even for the same function)

  State View:
    • Each round's proponent/opponent shows the other's argument as INPUT
    • Shows how agents evolve their positions across rounds
    • judge INPUT = summary of all rounds

  Time Travel:
    • Scrub through to watch the debate unfold round by round
    • See how arguments change between rounds

  Token Dashboard:
    • judge tallest bar (gets full debate context)
    • All debate agents roughly equal per round
    • Line chart shows token accumulation increasing each round

  Run Comparison:
    • 6+ rows: moderator, 4 debate agents (2 per side × 2 rounds), judge
    • All green

  Failure History:
    • Empty
"""
import asyncio
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from agentradar import radar, reset_run

load_dotenv()

llm = ChatOpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url=os.getenv("DEEPSEEK_BASE_URL"),
    model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
    temperature=0.8,  # higher temperature for more varied debate responses
)


@radar(tag="moderator", llm_input="Set the debate topic, rules, and opening positions")
async def moderator(motion: str) -> dict:
    """Set up the debate: define positions and rules."""
    message = llm.invoke(
        f"You are a debate moderator. Define the debate on: '{motion}'\n"
        f"Return: opening_for (1 sentence supporting), opening_against (1 sentence opposing), "
        f"and key_question (1 central question to resolve). Format as JSON."
    )
    return {
        "motion": motion,
        "setup": message.content,
        "rounds": 2,
    }


@radar(tag="proponent", llm_input=["moderator"])
async def proponent(motion: str, round_num: int, opponent_arg: str = "") -> dict:
    """Argue FOR the motion. Called once per round."""
    context = f"\nOpponent said: {opponent_arg}" if opponent_arg else ""
    message = llm.invoke(
        f"You argue FOR: '{motion}'\nRound {round_num}.{context}\n"
        f"Make a compelling 2-sentence argument with one concrete example. "
        f"Directly rebut the opponent if they argued."
    )
    return {
        "side": "FOR",
        "round": round_num,
        "argument": message.content,
    }


@radar(tag="opponent", llm_input=["moderator"])
async def opponent(motion: str, round_num: int, proponent_arg: str = "") -> dict:
    """Argue AGAINST the motion. Called once per round."""
    context = f"\nProponent said: {proponent_arg}" if proponent_arg else ""
    message = llm.invoke(
        f"You argue AGAINST: '{motion}'\nRound {round_num}.{context}\n"
        f"Make a compelling 2-sentence counter-argument with one concrete example. "
        f"Directly rebut the proponent if they argued."
    )
    return {
        "side": "AGAINST",
        "round": round_num,
        "argument": message.content,
    }


@radar(tag="judge", llm_input=["proponent", "opponent"])
async def judge(motion: str, debate_transcript: list) -> dict:
    """Final agent: judge the debate and declare a winner."""
    transcript = "\n".join(
        f"Round {r['round']} ({r['side']}): {r['argument']}"
        for r in debate_transcript
    )
    message = llm.invoke(
        f"Motion: '{motion}'\n\nDebate transcript:\n{transcript}\n\n"
        f"As an impartial judge:\n"
        f"1. Who argued more effectively? (FOR/AGAINST)\n"
        f"2. What was the strongest point made?\n"
        f"3. What was the weakest argument?\n"
        f"4. Final verdict with reasoning (2 sentences)."
    )
    return {
        "motion": motion,
        "verdict": message.content,
        "rounds_judged": len(debate_transcript) // 2,
        "status": "debate_complete",
    }


async def main():
    reset_run()
    motion = "Artificial General Intelligence will be developed before 2040"

    print(f'=== Debate: "{motion}" ===\n')

    print("[Setup] Moderator opening...")
    setup = await moderator(motion)
    print(f"  → Motion set, {setup['rounds']} rounds planned\n")

    debate_transcript = []

    for round_num in range(1, 3):
        print(f"[Round {round_num}] Running debate...")

        last_opponent = debate_transcript[-1]["argument"] if debate_transcript else ""
        last_proponent = debate_transcript[-2]["argument"] if len(debate_transcript) >= 2 else ""

        # Run both sides in parallel for each round
        pro_result, con_result = await asyncio.gather(
            proponent(motion, round_num, last_opponent),
            opponent(motion, round_num, last_proponent),
        )

        debate_transcript.append(pro_result)
        debate_transcript.append(con_result)

        print(f"  FOR:     {pro_result['argument'][:80]}...")
        print(f"  AGAINST: {con_result['argument'][:80]}...\n")

    print("[Judgment] Judge deliberating...")
    verdict = await judge(motion, debate_transcript)
    print(f"\n[Verdict]\n{verdict['verdict'][:300]}...")

    print(f"\n✓ Dashboard: {len(debate_transcript) + 2} nodes (moderator + 4 debate + judge), multi-round visible")


if __name__ == "__main__":
    asyncio.run(main())
    input("\nPress Enter to view dashboard (http://localhost:7111) or Ctrl+C to exit...")
