"""
04_error_handling.py — Intentional failure + recovery agent

Tests the Failure History view. One agent fails on purpose (bad API call),
a recovery agent catches it and produces a fallback result, then the pipeline
continues. The final tally has a mix of red (failed) and green (success) nodes.

Pipeline: validator → risky_agent (FAILS) → recovery_agent → summarizer

WHAT THE DASHBOARD SHOULD SHOW:
  Live Graph:
    • validator (green) → risky_agent (RED = error)
    • recovery_agent (green, parent = validator) → summarizer (green)
    • Red node pulses or has error badge

  State View:
    • Step 2 — risky_agent: shows ERROR details, traceback visible in metadata
    • Step 3 — recovery_agent: INPUT contains the error message, OUTPUT = fallback

  Time Travel:
    • Pause at step 2 to inspect the exact error before recovery happened

  Token Dashboard:
    • risky_agent bar = 0 tokens (failed before LLM call completed)
    • recovery_agent has tokens (fallback LLM call succeeded)

  Run Comparison:
    • risky_agent row: STATUS=FAILED, red background

  Failure History:
    • This run auto-saved as a failure snapshot
    • Shows run_id, timestamp, number of failed agents
    • Click to load and replay in Time Travel
"""
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.3,
)


@radar(tag="validator", llm_input="Validate that the input topic is a real subject worth researching")
def validator(topic: str) -> dict:
    """Step 1: Validate the input."""
    message = llm.invoke(f"In one sentence, confirm this is a valid research topic: {topic}")
    return {"topic": topic, "valid": True, "validation_note": message.content}


@radar(tag="risky_agent", llm_input=["validator"])
def risky_agent(validated: dict) -> str:
    """
    Step 2: This agent intentionally fails (simulates a bad tool call / API error).
    The dashboard will capture the error, traceback, and mark the node RED.
    """
    topic = validated["topic"]
    print(f"[Risky] Attempting unstable operation on: {topic}")
    # Simulate an unexpected failure mid-pipeline
    raise ValueError(
        f"External API timeout: could not fetch live data for '{topic}'. "
        "Status 503 — service temporarily unavailable."
    )


@radar(tag="recovery_agent", llm_input=["validator"])
def recovery_agent(validated: dict, error_message: str) -> str:
    """
    Step 3: Recovery — uses cached/fallback data when risky_agent fails.
    Parent is 'validator' (bypasses failed risky_agent in the graph).
    """
    topic = validated["topic"]
    print(f"[Recovery] Generating fallback for: {topic}")
    message = llm.invoke(
        f"The primary data source failed with: '{error_message}'.\n"
        f"Provide a reliable general-knowledge answer about: {topic}"
    )
    return f"[FALLBACK] {message.content}"


@radar(tag="summarizer", llm_input=["recovery_agent"])
def summarizer(content: str) -> dict:
    """Step 4: Summarize whatever content we have (fallback or real)."""
    message = llm.invoke(f"Summarize this content in 2 sentences:\n{content}")
    return {
        "summary": message.content,
        "source": "fallback" if content.startswith("[FALLBACK]") else "primary",
        "status": "complete",
    }


def main():
    reset_run()
    topic = "Real-time quantum entanglement communication networks"

    print("=== Error Handling + Recovery Pipeline ===\n")

    print("[1/4] Validating topic...")
    validated = validator(topic)
    print(f"  → Valid: {validated['valid']}\n")

    print("[2/4] Running risky agent (will fail)...")
    fallback_content = None
    try:
        risky_agent(validated)
    except ValueError as e:
        error_msg = str(e)
        print(f"  ✗ Failed as expected: {error_msg[:80]}...\n")

        print("[3/4] Running recovery agent...")
        fallback_content = recovery_agent(validated, error_msg)
        print(f"  → Fallback generated: {fallback_content[:80]}...\n")

    print("[4/4] Summarizing...")
    result = summarizer(fallback_content)
    print(f"  → Summary ({result['source']}): {result['summary'][:100]}...")

    print("\n✓ Dashboard: risky_agent node RED, failure auto-saved to Failure History")


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