"""Leaderboard HTML generator with SVG radar charts.

Generates a self-contained HTML page from capability profile data,
featuring per-agent radar charts and an overlay comparison chart.
"""

import math
from datetime import datetime, timezone

# 3 GA axes at 120-degree intervals, starting from top
AXIS_KEYS = ["code_quality", "error_resilience", "instruction_resilience"]
AXIS_LABELS = ["Code Quality", "Error Resilience", "Instr. Resilience"]
AXIS_LABELS_SHORT = ["CQ", "ER", "IR"]
_ANGLES = [-math.pi / 2 + i * 2 * math.pi / 3 for i in range(3)]

AGENT_COLORS = {
    "claude": {"fill": "rgba(74, 222, 128, 0.2)", "stroke": "#4ade80"},
    "codex": {"fill": "rgba(96, 165, 250, 0.2)", "stroke": "#60a5fa"},
    "gemini": {"fill": "rgba(251, 146, 60, 0.2)", "stroke": "#fb923c"},
    "copilot": {"fill": "rgba(244, 114, 182, 0.2)", "stroke": "#f472b6"},
}

# Distinct colors for overlay chart (top-N agents)
OVERLAY_PALETTE = ["#4ade80", "#60a5fa", "#f472b6", "#fbbf24", "#a78bfa"]

MODEL_DISPLAY = {
    "claude-sonnet-4-5-20250929": "Sonnet 4.5",
    "claude-haiku-4-5-20251001": "Haiku 4.5",
    "claude-opus-4-6": "Opus 4.6",
    "claude-opus-4.6": "Opus 4.6",
    "gpt-5.2": "GPT-5.2",
    "gpt-4o": "GPT-4o",
    "gpt-4o-mini": "GPT-4o Mini",
    "gpt-4.1": "GPT-4.1",
    "o3": "o3",
    "gemini-2.5-pro": "2.5 Pro",
    "gemini-2.5-flash": "2.5 Flash",
    "gemini-2.0-flash": "2.0 Flash",
    "gemini-3-pro-preview": "3 Pro",
}

AGENT_DISPLAY = {
    "claude": "Claude",
    "codex": "Codex",
    "gemini": "Gemini",
    "copilot": "Copilot",
}

GRADE_BG = {
    "A+": "linear-gradient(135deg, #fbbf24, #f59e0b)",
    "A": "linear-gradient(135deg, #4ade80, #22c55e)",
    "B": "linear-gradient(135deg, #60a5fa, #3b82f6)",
    "C": "linear-gradient(135deg, #a78bfa, #8b5cf6)",
    "D": "linear-gradient(135deg, #fb923c, #f97316)",
    "F": "linear-gradient(135deg, #ef4444, #dc2626)",
}

GRADE_TEXT = {
    "A+": "#1a1a1a",
    "A": "#1a1a1a",
    "B": "#fff",
    "C": "#fff",
    "D": "#1a1a1a",
    "F": "#fff",
}


def _display_model(model: str) -> str:
    return MODEL_DISPLAY.get(model, model)


def _display_agent(agent: str) -> str:
    return AGENT_DISPLAY.get(agent, agent.title())


def _axis_scores(profile: dict) -> list[float]:
    """Extract 3 axis scores from a profile."""
    scores = []
    for key in AXIS_KEYS:
        axis = profile.get("axes", {}).get(key)
        scores.append(axis["score"] if axis else 0)
    return scores


def _svg_grid(cx: float, cy: float, max_r: float) -> str:
    """SVG grid: concentric triangles + axis lines."""
    parts = []
    for pct in [0.25, 0.5, 0.75, 1.0]:
        r = max_r * pct
        pts = " ".join(
            f"{cx + r * math.cos(a):.1f},{cy + r * math.sin(a):.1f}"
            for a in _ANGLES
        )
        op = 0.3 if pct == 1.0 else 0.12
        parts.append(
            f'<polygon points="{pts}" fill="none" stroke="#4b5563" '
            f'stroke-width="0.5" opacity="{op}"/>'
        )
    for a in _ANGLES:
        parts.append(
            f'<line x1="{cx}" y1="{cy}" '
            f'x2="{cx + max_r * math.cos(a):.1f}" '
            f'y2="{cy + max_r * math.sin(a):.1f}" '
            f'stroke="#4b5563" stroke-width="0.5" opacity="0.25"/>'
        )
    return "\n      ".join(parts)


def _svg_data(
    scores: list[float],
    cx: float,
    cy: float,
    max_r: float,
    fill: str,
    stroke: str,
    stroke_w: float = 2,
    dot_r: float = 3,
) -> str:
    """SVG polygon + dots for one data series."""
    points = []
    for i, s in enumerate(scores):
        r = max_r * min(s, 100) / 100
        points.append((cx + r * math.cos(_ANGLES[i]), cy + r * math.sin(_ANGLES[i])))
    pts_str = " ".join(f"{x:.1f},{y:.1f}" for x, y in points)
    parts = [
        f'<polygon points="{pts_str}" fill="{fill}" stroke="{stroke}" '
        f'stroke-width="{stroke_w}"/>'
    ]
    for x, y in points:
        parts.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{dot_r}" fill="{stroke}"/>')
    return "\n      ".join(parts)


def _svg_labels(
    cx: float, cy: float, max_r: float, labels: list[str], font_size: int = 10
) -> str:
    """SVG text labels at axis endpoints."""
    label_r = max_r + 14
    parts = []
    for i, label in enumerate(labels):
        x = cx + label_r * math.cos(_ANGLES[i])
        y = cy + label_r * math.sin(_ANGLES[i])
        anchor = "middle" if i == 0 else ("start" if i == 1 else "end")
        dy = "-6" if i == 0 else "14"
        parts.append(
            f'<text x="{x:.1f}" y="{y:.1f}" text-anchor="{anchor}" dy="{dy}" '
            f'fill="#9ca3af" font-size="{font_size}" font-family="sans-serif">'
            f"{label}</text>"
        )
    return "\n      ".join(parts)


def _card_svg(profile: dict) -> str:
    """Small radar chart for a profile card (160x140)."""
    w, h = 160, 140
    cx, cy = w / 2, h / 2 + 8
    max_r = 48

    agent = profile.get("agent", "unknown")
    colors = AGENT_COLORS.get(agent, AGENT_COLORS["claude"])
    scores = _axis_scores(profile)

    return (
        f'<svg viewBox="0 0 {w} {h}" width="{w}" height="{h}" '
        f'xmlns="http://www.w3.org/2000/svg">\n'
        f"      {_svg_grid(cx, cy, max_r)}\n"
        f"      {_svg_data(scores, cx, cy, max_r, colors['fill'], colors['stroke'])}\n"
        f"      {_svg_labels(cx, cy, max_r, AXIS_LABELS_SHORT, font_size=9)}\n"
        f"    </svg>"
    )


def _overlay_svg(profiles: list[dict]) -> str:
    """Large overlay radar chart for top-N comparison (500x400)."""
    w, h = 500, 400
    cx, cy = w / 2, h / 2 + 10
    max_r = 140

    grid = _svg_grid(cx, cy, max_r)
    labels = _svg_labels(cx, cy, max_r, AXIS_LABELS, font_size=13)

    data_parts = []
    for i, p in enumerate(profiles):
        color = OVERLAY_PALETTE[i % len(OVERLAY_PALETTE)]
        fill = color.replace(")", ", 0.08)").replace("rgb", "rgba") if "rgb" in color else f"{color}14"
        scores = _axis_scores(p)
        data_parts.append(
            _svg_data(scores, cx, cy, max_r, fill, color, stroke_w=2, dot_r=4)
        )

    return (
        f'<svg viewBox="0 0 {w} {h}" width="100%" height="{h}" '
        f'xmlns="http://www.w3.org/2000/svg" '
        f'style="max-width:{w}px">\n'
        f"      {grid}\n"
        f'      {"      ".join(data_parts)}\n'
        f"      {labels}\n"
        f"    </svg>"
    )


def _grade_badge_style(grade: str) -> str:
    bg = GRADE_BG.get(grade, "linear-gradient(135deg, #6b7280, #4b5563)")
    color = GRADE_TEXT.get(grade, "#fff")
    return f"background:{bg};color:{color}"


def profiles_to_leaderboard_html(profiles: list[dict]) -> str:
    """Generate a self-contained HTML leaderboard with SVG radar charts."""
    profiles = sorted(profiles, key=lambda p: p["composite_score"], reverse=True)
    now = datetime.now(timezone.utc).strftime("%B %d, %Y")
    suite_ver = profiles[0].get("source", {}).get("suite_version", "1.3.0") if profiles else "1.3.0"
    mean_composite = sum(p["composite_score"] for p in profiles) / len(profiles) if profiles else 0

    # Top 5 for overlay
    top5 = profiles[:5]
    overlay = _overlay_svg(top5)

    # Overlay legend
    legend_items = []
    for i, p in enumerate(top5):
        color = OVERLAY_PALETTE[i % len(OVERLAY_PALETTE)]
        agent = _display_agent(p.get("agent", ""))
        model = _display_model(p.get("model", ""))
        legend_items.append(
            f'<span class="legend-item">'
            f'<span class="legend-dot" style="background:{color}"></span>'
            f'{agent} {model}</span>'
        )
    legend_html = " ".join(legend_items)

    # Profile cards
    cards = []
    for rank, p in enumerate(profiles, 1):
        agent = _display_agent(p.get("agent", ""))
        model = _display_model(p.get("model", ""))
        grade = p.get("grade", "?")
        composite = p.get("composite_score", 0)
        svg = _card_svg(p)
        scores = _axis_scores(p)

        rank_class = f"rank-{rank}" if rank <= 3 else ""
        badge_style = _grade_badge_style(grade)
        agent_key = p.get("agent", "unknown")
        agent_color = AGENT_COLORS.get(agent_key, AGENT_COLORS["claude"])["stroke"]

        cards.append(f"""      <div class="card">
        <div class="card-header">
          <span class="card-rank {rank_class}">#{rank}</span>
          <span class="card-grade" style="{badge_style}">{grade}</span>
        </div>
        <div class="card-radar">{svg}</div>
        <div class="card-info">
          <div class="card-agent" style="color:{agent_color}">{agent}</div>
          <div class="card-model">{model}</div>
          <div class="card-score">{composite:.1f}</div>
        </div>
        <div class="card-axes">
          <div class="axis-row"><span class="axis-label">CQ</span><div class="axis-bar"><div class="axis-fill" style="width:{scores[0]}%;background:{agent_color}"></div></div><span class="axis-val">{scores[0]:.1f}</span></div>
          <div class="axis-row"><span class="axis-label">ER</span><div class="axis-bar"><div class="axis-fill" style="width:{scores[1]}%;background:{agent_color}"></div></div><span class="axis-val">{scores[1]:.1f}</span></div>
          <div class="axis-row"><span class="axis-label">IR</span><div class="axis-bar"><div class="axis-fill" style="width:{scores[2]}%;background:{agent_color}"></div></div><span class="axis-val">{scores[2]:.1f}</span></div>
        </div>
      </div>""")

    cards_html = "\n".join(cards)

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Janus Labs — Agent Capability Profiles</title>
  <style>
    * {{ box-sizing: border-box; margin: 0; padding: 0; }}
    body {{
      font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;
      background: linear-gradient(135deg, #0a0c10 0%, #151922 50%, #1a1f2e 100%);
      color: #e6e6e6;
      min-height: 100vh;
      padding: 40px 20px;
    }}
    .container {{ max-width: 1100px; margin: 0 auto; }}

    /* Header */
    header {{ text-align: center; margin-bottom: 40px; }}
    .logo {{ font-size: 13px; letter-spacing: 4px; text-transform: uppercase; color: #6b7280; margin-bottom: 8px; }}
    h1 {{
      font-size: 38px; font-weight: 700;
      background: linear-gradient(90deg, #60a5fa, #a78bfa, #f472b6);
      -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
      margin-bottom: 10px;
    }}
    .subtitle {{ color: #9ca3af; font-size: 15px; }}
    .date-badge {{
      display: inline-block; background: rgba(99, 102, 241, 0.15);
      border: 1px solid rgba(99, 102, 241, 0.3); border-radius: 20px;
      padding: 5px 14px; font-size: 12px; color: #818cf8; margin-top: 14px;
    }}

    /* Summary cards */
    .summary {{ display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 40px; }}
    .summary-card {{
      background: #151922; border: 1px solid #2a2f3a; border-radius: 12px;
      padding: 18px; text-align: center;
    }}
    .summary-val {{
      font-size: 28px; font-weight: 700;
      background: linear-gradient(90deg, #60a5fa, #a78bfa);
      -webkit-background-clip: text; -webkit-text-fill-color: transparent;
    }}
    .summary-lbl {{ font-size: 11px; color: #6b7280; margin-top: 4px; text-transform: uppercase; letter-spacing: 1px; }}

    /* Overlay section */
    .overlay-section {{
      background: #151922; border: 1px solid #2a2f3a; border-radius: 16px;
      padding: 28px; margin-bottom: 40px; text-align: center;
    }}
    .overlay-section h2 {{ font-size: 18px; color: #9ca3af; margin-bottom: 20px; font-weight: 500; }}
    .overlay-chart {{ display: flex; justify-content: center; }}
    .legend {{ display: flex; justify-content: center; gap: 20px; margin-top: 16px; flex-wrap: wrap; }}
    .legend-item {{ display: flex; align-items: center; gap: 6px; font-size: 13px; color: #9ca3af; }}
    .legend-dot {{ width: 10px; height: 10px; border-radius: 50%; display: inline-block; }}

    /* Profile cards grid */
    .cards-header {{ font-size: 18px; color: #9ca3af; margin-bottom: 20px; font-weight: 500; }}
    .cards {{ display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 40px; }}
    .card {{
      background: #151922; border: 1px solid #2a2f3a; border-radius: 14px;
      padding: 16px; transition: border-color 0.2s, transform 0.2s;
    }}
    .card:hover {{ border-color: #4b5563; transform: translateY(-2px); }}
    .card-header {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }}
    .card-rank {{ font-size: 15px; font-weight: 700; color: #6b7280; }}
    .card-rank.rank-1 {{ color: #fbbf24; }}
    .card-rank.rank-2 {{ color: #94a3b8; }}
    .card-rank.rank-3 {{ color: #d97706; }}
    .card-grade {{
      font-size: 14px; font-weight: 700; padding: 3px 10px;
      border-radius: 6px; display: inline-flex; align-items: center;
    }}
    .card-radar {{ display: flex; justify-content: center; margin: 4px 0; }}
    .card-info {{ text-align: center; margin-bottom: 10px; }}
    .card-agent {{ font-size: 16px; font-weight: 600; }}
    .card-model {{ font-size: 12px; color: #6b7280; margin-top: 2px; }}
    .card-score {{ font-size: 28px; font-weight: 700; color: #fff; margin-top: 4px; }}
    .card-axes {{ display: flex; flex-direction: column; gap: 5px; }}
    .axis-row {{ display: flex; align-items: center; gap: 6px; }}
    .axis-label {{ font-size: 10px; color: #6b7280; width: 20px; text-align: right; }}
    .axis-bar {{ flex: 1; height: 4px; background: #2a2f3a; border-radius: 2px; overflow: hidden; }}
    .axis-fill {{ height: 100%; border-radius: 2px; transition: width 0.8s ease-out; }}
    .axis-val {{ font-size: 10px; color: #9ca3af; width: 30px; }}

    /* Footer */
    footer {{ text-align: center; padding: 24px; color: #6b7280; font-size: 12px; }}
    footer a {{ color: #818cf8; text-decoration: none; }}

    /* Responsive */
    @media (max-width: 900px) {{
      .cards {{ grid-template-columns: repeat(2, 1fr); }}
      .summary {{ grid-template-columns: repeat(2, 1fr); }}
    }}
    @media (max-width: 500px) {{
      .cards {{ grid-template-columns: 1fr; }}
      .summary {{ grid-template-columns: repeat(2, 1fr); }}
      h1 {{ font-size: 28px; }}
    }}
  </style>
</head>
<body>
  <div class="container">
    <header>
      <div class="logo">Janus Labs</div>
      <h1>Agent Capability Profiles</h1>
      <p class="subtitle">3-Axis Radar Comparison &mdash; refactor-storm v{suite_ver}</p>
      <div class="date-badge">{now} &bull; {len(profiles)} agents &bull; 7 behaviors</div>
    </header>

    <div class="summary">
      <div class="summary-card"><div class="summary-val">{len(profiles)}</div><div class="summary-lbl">Profiles</div></div>
      <div class="summary-card"><div class="summary-val">{mean_composite:.1f}</div><div class="summary-lbl">Mean Composite</div></div>
      <div class="summary-card"><div class="summary-val">{profiles[0]["composite_score"]:.1f}</div><div class="summary-lbl">Top Score</div></div>
      <div class="summary-card"><div class="summary-val">{profiles[0]["grade"]}</div><div class="summary-lbl">Top Grade</div></div>
    </div>

    <div class="overlay-section">
      <h2>Top 5 Comparison</h2>
      <div class="overlay-chart">{overlay}</div>
      <div class="legend">{legend_html}</div>
    </div>

    <div class="cards-header">All Profiles (ranked by composite score)</div>
    <div class="cards">
{cards_html}
    </div>

    <footer>
      <p>Generated by <a href="https://pypi.org/project/janus-labs/">Janus Labs</a> &mdash; 3DMark for AI Agents</p>
      <p style="margin-top: 6px; font-size: 11px;">refactor-storm v{suite_ver} &bull; GEval scoring &bull; 3-axis-ga-v1 schema</p>
    </footer>
  </div>
</body>
</html>
"""
