"""
07_code_review_crew.py — Multi-agent code review system

A realistic engineering crew: planner breaks the code into concerns,
3 specialists review in parallel (security, performance, style),
a lead synthesizes findings, and a patcher writes the fix plan.

Pipeline:
  review_planner
    ├─ security_reviewer  (parallel)
    ├─ performance_reviewer (parallel)
    └─ style_reviewer     (parallel)
  → review_lead  (merge: synthesize all reviews)
  → patch_planner

WHAT THE DASHBOARD SHOULD SHOW:
  Live Graph:
    • review_planner → 3 parallel reviewers → review_lead → patch_planner
    • Parallel band labeled across security/performance/style column
    • 6 total nodes, clear fan-out and fan-in visible

  State View:
    • review_planner: OUTPUT = {concerns: [...], priority: "high|medium|low"}
    • security_reviewer: OUTPUT = {issues: [...], severity: "critical|medium|low"}
    • performance_reviewer: OUTPUT = {bottlenecks: [...], suggestions: [...]}
    • style_reviewer: OUTPUT = {violations: [...], score: 0-10}
    • review_lead: shows unified INPUT from all 3 reviews, OUTPUT = final verdict
    • patch_planner: OUTPUT = numbered action plan

  Time Travel:
    • Rewind to see each reviewer's raw output before synthesis
    • Interesting: all 3 reviewers ran at the same timestamp range

  Token Dashboard:
    • review_lead tallest (receives all 3 reviews as context)
    • All 3 parallel reviewers roughly equal

  Run Comparison:
    • Parallel reviewers show overlapping time windows
    • patch_planner shows longest generation time (detailed plan)
"""
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.3,
)

# Sample code to review
SAMPLE_CODE = """
def get_user_data(user_id):
    import sqlite3
    conn = sqlite3.connect('users.db')
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor = conn.cursor()
    cursor.execute(query)
    result = cursor.fetchall()
    return result

def process_data(data):
    output = []
    for i in range(len(data)):
        for j in range(len(data)):
            if data[i] == data[j] and i != j:
                output.append(data[i])
    return output

def render_profile(user):
    html = "<div>" + user['name'] + "</div>"
    return html
"""


@radar(tag="review_planner", llm_input="Identify the main concern areas in this code for review")
async def review_planner(code: str) -> dict:
    """Step 1: Plan the review — identify what areas need attention."""
    message = llm.invoke(
        f"Identify the 3 main concern areas in this code (security, performance, style). "
        f"For each, rate priority (HIGH/MEDIUM/LOW):\n```python\n{code}\n```"
    )
    return {
        "code": code,
        "plan": message.content,
        "reviewers": ["security", "performance", "style"],
    }


@radar(tag="security_reviewer", llm_input=["review_planner"])
async def security_reviewer(planned: dict) -> dict:
    """Parallel reviewer A: security vulnerabilities."""
    await asyncio.sleep(0.2)
    message = llm.invoke(
        f"Review this code for security vulnerabilities (SQL injection, XSS, auth issues). "
        f"List each issue with severity (CRITICAL/MEDIUM/LOW):\n```python\n{planned['code']}\n```"
    )
    issues = [l.strip() for l in message.content.split("\n") if l.strip()]
    critical = [i for i in issues if "CRITICAL" in i.upper()]
    return {
        "domain": "security",
        "issues": issues,
        "critical_count": len(critical),
        "raw": message.content,
    }


@radar(tag="performance_reviewer", llm_input=["review_planner"])
async def performance_reviewer(planned: dict) -> dict:
    """Parallel reviewer B: performance bottlenecks."""
    await asyncio.sleep(0.3)
    message = llm.invoke(
        f"Review this code for performance issues (O(n²) loops, N+1 queries, memory leaks). "
        f"List each bottleneck with estimated impact:\n```python\n{planned['code']}\n```"
    )
    return {
        "domain": "performance",
        "bottlenecks": [l.strip() for l in message.content.split("\n") if l.strip()],
        "raw": message.content,
    }


@radar(tag="style_reviewer", llm_input=["review_planner"])
async def style_reviewer(planned: dict) -> dict:
    """Parallel reviewer C: code style and maintainability."""
    await asyncio.sleep(0.15)
    message = llm.invoke(
        f"Review this code for style issues (naming, documentation, PEP8, readability). "
        f"Give a quality score 1-10 and list top 3 violations:\n```python\n{planned['code']}\n```"
    )
    return {
        "domain": "style",
        "violations": [l.strip() for l in message.content.split("\n") if l.strip()],
        "raw": message.content,
    }


@radar(tag="review_lead", llm_input=["security_reviewer", "performance_reviewer", "style_reviewer"])
async def review_lead(all_reviews: list) -> dict:
    """
    Merge point: synthesize security + performance + style reviews.
    all_reviews = [security_result, performance_result, style_result]
    """
    security, performance, style = all_reviews[0], all_reviews[1], all_reviews[2]
    combined = (
        f"SECURITY:\n{security['raw']}\n\n"
        f"PERFORMANCE:\n{performance['raw']}\n\n"
        f"STYLE:\n{style['raw']}"
    )
    message = llm.invoke(
        f"As lead reviewer, synthesize these three reviews into:\n"
        f"1. Overall verdict (APPROVE/NEEDS_CHANGES/REJECT)\n"
        f"2. Top 3 must-fix issues\n"
        f"3. Optional improvements\n\n{combined}"
    )
    return {
        "verdict": "NEEDS_CHANGES",
        "synthesis": message.content,
        "critical_security": security["critical_count"],
        "reviews_merged": 3,
    }


@radar(tag="patch_planner", llm_input=["review_lead"])
async def patch_planner(lead_review: dict) -> dict:
    """Final step: create an actionable patch plan from the lead review."""
    message = llm.invoke(
        f"Create a numbered, prioritized action plan to fix all issues identified:\n"
        f"{lead_review['synthesis']}"
    )
    steps = [l.strip() for l in message.content.split("\n") if l.strip()]
    return {
        "verdict": lead_review["verdict"],
        "action_plan": steps,
        "estimated_fixes": len([s for s in steps if s[0].isdigit()]),
        "status": "review_complete",
    }


async def main():
    reset_run()

    print("=== Code Review Crew (3 Parallel Reviewers + Merge) ===\n")
    print("[Code snippet loaded — 3 reviewers analyzing in parallel]\n")

    print("[1] Planning review...")
    planned = await review_planner(SAMPLE_CODE)
    print(f"  → Review areas: {', '.join(planned['reviewers'])}\n")

    print("[2] Running parallel reviews (security + performance + style)...")
    sec, perf, style = await asyncio.gather(
        security_reviewer(planned),
        performance_reviewer(planned),
        style_reviewer(planned),
    )
    print(f"  → Security: {sec['critical_count']} critical issues")
    print(f"  → Performance: {len(perf['bottlenecks'])} bottlenecks")
    print(f"  → Style: {len(style['violations'])} violations\n")

    print("[3] Lead review (merging all...)...")
    verdict = await review_lead([sec, perf, style])
    print(f"  → Verdict: {verdict['verdict']}\n")

    print("[4] Creating patch plan...")
    plan = await patch_planner(verdict)
    print(f"  → {plan['estimated_fixes']} action items")
    print(f"  → Status: {plan['status']}")

    print("\n✓ Dashboard: 6-node graph, parallel band, review_lead is 3-parent merge point")


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