#!/usr/bin/env python3
"""
Build the 8-slide Retrieval Benchmark Rerun PDF for leadership.
Uses matplotlib for slide rendering with a dark professional theme.
Embeds figures from figures/ directory.
"""

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from matplotlib.backends.backend_pdf import PdfPages
from pathlib import Path
import textwrap

# ── Paths ──────────────────────────────────────────────────────────────
ROOT = Path(__file__).resolve().parent.parent
FIG_DIR = ROOT / "figures"
OUT_PDF = ROOT / "Retrieval_Benchmark_Rerun_Leadership.pdf"

# ── Theme ──────────────────────────────────────────────────────────────
BG       = "#0d1b2a"   # deep navy
BG_CARD  = "#1b2d45"   # slightly lighter card
ACCENT   = "#00b4d8"   # bright cyan accent
ACCENT2  = "#48cae4"   # lighter cyan
GOLD     = "#ffd60a"   # gold for callout numbers
WHITE    = "#e0e1dd"
MUTED    = "#778da9"
RED_SOFT = "#ef476f"

SLIDE_W, SLIDE_H = 16, 9  # 16:9

def new_slide():
    fig = plt.figure(figsize=(SLIDE_W, SLIDE_H), facecolor=BG)
    fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
    return fig

def add_footer(fig, slide_num, total=8):
    fig.text(0.96, 0.025, f"{slide_num}/{total}",
             fontsize=11, color=MUTED, ha="right", va="bottom",
             fontfamily="sans-serif")
    fig.text(0.04, 0.025, "Confidential",
             fontsize=9, color=MUTED, ha="left", va="bottom",
             fontfamily="sans-serif", style="italic")

# ══════════════════════════════════════════════════════════════════════
# SLIDE 1 — Title
# ══════════════════════════════════════════════════════════════════════
def slide_title():
    fig = new_slide()
    # Decorative accent bar
    ax = fig.add_axes([0.06, 0.42, 0.005, 0.22])
    ax.set_facecolor(ACCENT)
    ax.set_xticks([]); ax.set_yticks([])
    for spine in ax.spines.values(): spine.set_visible(False)

    fig.text(0.09, 0.62, "Retrieval Benchmark Rerun",
             fontsize=48, fontweight="bold", color=WHITE,
             fontfamily="sans-serif", va="bottom")
    fig.text(0.09, 0.54, "Updated Results",
             fontsize=42, fontweight="light", color=ACCENT,
             fontfamily="sans-serif", va="bottom")

    fig.text(0.09, 0.40, "April 10, 2026  |  Leadership Update",
             fontsize=20, color=MUTED, fontfamily="sans-serif")
    fig.text(0.09, 0.33, "doany Retrieval Team",
             fontsize=18, color=MUTED, fontfamily="sans-serif")

    # Bottom accent line
    ax2 = fig.add_axes([0.06, 0.12, 0.35, 0.003])
    ax2.set_facecolor(ACCENT)
    ax2.set_xticks([]); ax2.set_yticks([])
    for spine in ax2.spines.values(): spine.set_visible(False)

    add_footer(fig, 1)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 2 — Why We Reran
# ══════════════════════════════════════════════════════════════════════
def slide_why():
    fig = new_slide()
    fig.text(0.06, 0.88, "Why We Reran", fontsize=36, fontweight="bold",
             color=WHITE, fontfamily="sans-serif")
    # Accent underline
    ax_line = fig.add_axes([0.06, 0.855, 0.18, 0.003])
    ax_line.set_facecolor(ACCENT); ax_line.set_xticks([]); ax_line.set_yticks([])
    for s in ax_line.spines.values(): s.set_visible(False)

    # Bug callout card
    ax_card = fig.add_axes([0.06, 0.48, 0.40, 0.32])
    ax_card.set_facecolor(BG_CARD); ax_card.set_xticks([]); ax_card.set_yticks([])
    for s in ax_card.spines.values(): s.set_color(ACCENT); s.set_linewidth(1.5)

    fig.text(0.08, 0.76, "Chunking Overlap Bug  (PR #347)",
             fontsize=22, fontweight="bold", color=RED_SOFT, fontfamily="sans-serif")
    fig.text(0.08, 0.68, "Silently dropped ~8% of document chunks\nin the indexing pipeline",
             fontsize=18, color=WHITE, fontfamily="sans-serif", linespacing=1.5)
    fig.text(0.08, 0.54, "Disproportionately hurt models trained\nwith overlapping context windows",
             fontsize=16, color=MUTED, fontfamily="sans-serif", linespacing=1.5)

    # Key facts — right side
    facts = [
        ("Previous run", "Mar 28, 2026"),
        ("Rerun", "Apr 10, 2026"),
        ("Dataset", "doany-skills-eval-v2"),
        ("Queries", "4,200"),
        ("Documents", "58,000"),
    ]
    y = 0.76
    for label, value in facts:
        fig.text(0.56, y, label, fontsize=16, color=MUTED, fontfamily="sans-serif")
        fig.text(0.78, y, value, fontsize=16, fontweight="bold", color=WHITE,
                 fontfamily="sans-serif")
        y -= 0.065

    add_footer(fig, 2)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 3 — Headline Results (with figure)
# ══════════════════════════════════════════════════════════════════════
def slide_headline():
    fig = new_slide()
    fig.text(0.06, 0.88, "Headline Results", fontsize=36, fontweight="bold",
             color=WHITE, fontfamily="sans-serif")
    ax_line = fig.add_axes([0.06, 0.855, 0.22, 0.003])
    ax_line.set_facecolor(ACCENT); ax_line.set_xticks([]); ax_line.set_yticks([])
    for s in ax_line.spines.values(): s.set_visible(False)

    # Embed figure — left/center
    img_path = FIG_DIR / "retrieval_accuracy.png"
    ax_img = fig.add_axes([0.04, 0.08, 0.55, 0.72])
    ax_img.set_facecolor(BG)
    ax_img.set_xticks([]); ax_img.set_yticks([])
    for s in ax_img.spines.values(): s.set_visible(False)
    if img_path.exists():
        img = mpimg.imread(str(img_path))
        ax_img.imshow(img, aspect="auto")

    # Callout numbers — right side
    fig.text(0.66, 0.76, "84.7%", fontsize=56, fontweight="bold", color=GOLD,
             fontfamily="sans-serif")
    fig.text(0.66, 0.69, "doany-ret-v3  Top-5 Accuracy",
             fontsize=16, color=WHITE, fontfamily="sans-serif")

    fig.text(0.66, 0.55, "+3.5pp", fontsize=44, fontweight="bold", color=ACCENT,
             fontfamily="sans-serif")
    fig.text(0.66, 0.49, "vs. previous run (81.2%)",
             fontsize=15, color=MUTED, fontfamily="sans-serif")

    fig.text(0.66, 0.36, "5.8pp", fontsize=40, fontweight="bold", color=ACCENT2,
             fontfamily="sans-serif")
    fig.text(0.66, 0.30, "lead over BGE-M3 (was 3.1pp)",
             fontsize=15, color=MUTED, fontfamily="sans-serif")

    add_footer(fig, 3)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 4 — Recall@K Deep Dive
# ══════════════════════════════════════════════════════════════════════
def slide_recall():
    fig = new_slide()
    fig.text(0.06, 0.88, "Recall@K Deep Dive", fontsize=36, fontweight="bold",
             color=WHITE, fontfamily="sans-serif")
    ax_line = fig.add_axes([0.06, 0.855, 0.24, 0.003])
    ax_line.set_facecolor(ACCENT); ax_line.set_xticks([]); ax_line.set_yticks([])
    for s in ax_line.spines.values(): s.set_visible(False)

    # Embed figure — center
    img_path = FIG_DIR / "recall_at_k.png"
    ax_img = fig.add_axes([0.04, 0.08, 0.55, 0.72])
    ax_img.set_facecolor(BG)
    ax_img.set_xticks([]); ax_img.set_yticks([])
    for s in ax_img.spines.values(): s.set_visible(False)
    if img_path.exists():
        img = mpimg.imread(str(img_path))
        ax_img.imshow(img, aspect="auto")

    # Callout — right
    fig.text(0.66, 0.74, "92%", fontsize=56, fontweight="bold", color=GOLD,
             fontfamily="sans-serif")
    fig.text(0.66, 0.67, "Recall@10 for doany-ret-v3",
             fontsize=16, color=WHITE, fontfamily="sans-serif")
    fig.text(0.66, 0.61, "up from 88% (+4pp)",
             fontsize=15, color=MUTED, fontfamily="sans-serif")

    # Secondary callout
    fig.text(0.66, 0.46, "BM25 Unaffected", fontsize=22, fontweight="bold",
             color=MUTED, fontfamily="sans-serif")
    fig.text(0.66, 0.40, "Sparse retrieval doesn't depend\non chunk overlap",
             fontsize=14, color=MUTED, fontfamily="sans-serif", linespacing=1.5)

    # Mini table — right side
    models = [
        ("doany-ret-v3", "92%", GOLD),
        ("BGE-M3", "87%", WHITE),
        ("E5-Large", "86%", WHITE),
        ("ColBERT v2", "84%", WHITE),
        ("BM25", "74%", MUTED),
    ]
    y = 0.28
    fig.text(0.66, y + 0.04, "Recall@10 Leaderboard", fontsize=14,
             fontweight="bold", color=ACCENT, fontfamily="sans-serif")
    for name, val, clr in models:
        fig.text(0.66, y, name, fontsize=13, color=MUTED, fontfamily="sans-serif")
        fig.text(0.88, y, val, fontsize=13, fontweight="bold", color=clr,
                 fontfamily="sans-serif", ha="right")
        y -= 0.04

    add_footer(fig, 4)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 5 — Latency Profile
# ══════════════════════════════════════════════════════════════════════
def slide_latency():
    fig = new_slide()
    fig.text(0.06, 0.88, "Latency Profile", fontsize=36, fontweight="bold",
             color=WHITE, fontfamily="sans-serif")
    ax_line = fig.add_axes([0.06, 0.855, 0.19, 0.003])
    ax_line.set_facecolor(ACCENT); ax_line.set_xticks([]); ax_line.set_yticks([])
    for s in ax_line.spines.values(): s.set_visible(False)

    # Embed figure
    img_path = FIG_DIR / "latency_comparison.png"
    ax_img = fig.add_axes([0.04, 0.08, 0.55, 0.72])
    ax_img.set_facecolor(BG)
    ax_img.set_xticks([]); ax_img.set_yticks([])
    for s in ax_img.spines.values(): s.set_visible(False)
    if img_path.exists():
        img = mpimg.imread(str(img_path))
        ax_img.imshow(img, aspect="auto")

    # Key message
    fig.text(0.66, 0.76, "Zero Overhead", fontsize=36, fontweight="bold",
             color=GOLD, fontfamily="sans-serif")
    fig.text(0.66, 0.69, "Fix was index-side only\nNo inference latency change",
             fontsize=16, color=WHITE, fontfamily="sans-serif", linespacing=1.5)

    # doany-ret-v3 stats
    fig.text(0.66, 0.52, "doany-ret-v3", fontsize=18, fontweight="bold",
             color=ACCENT, fontfamily="sans-serif")

    stats = [("p50", "34 ms"), ("p95", "71 ms")]
    y = 0.44
    for label, val in stats:
        fig.text(0.66, y, label, fontsize=20, color=MUTED, fontfamily="sans-serif")
        fig.text(0.78, y, val, fontsize=20, fontweight="bold", color=WHITE,
                 fontfamily="sans-serif")
        y -= 0.06

    fig.text(0.66, 0.27, "Fastest neural retriever", fontsize=16,
             fontweight="bold", color=ACCENT2, fontfamily="sans-serif")
    fig.text(0.66, 0.22, "2x faster than E5-Large (p50=62ms)\n1.7x faster than BGE-M3 (p50=58ms)",
             fontsize=13, color=MUTED, fontfamily="sans-serif", linespacing=1.5)

    add_footer(fig, 5)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 6 — Related Work
# ══════════════════════════════════════════════════════════════════════
def slide_related():
    fig = new_slide()
    fig.text(0.06, 0.88, "Related Work & Benchmarks", fontsize=36, fontweight="bold",
             color=WHITE, fontfamily="sans-serif")
    ax_line = fig.add_axes([0.06, 0.855, 0.30, 0.003])
    ax_line.set_facecolor(ACCENT); ax_line.set_xticks([]); ax_line.set_yticks([])
    for s in ax_line.spines.values(): s.set_visible(False)

    # Two-column layout
    # Left column: Key models
    fig.text(0.06, 0.77, "Retrieval Models", fontsize=20, fontweight="bold",
             color=ACCENT, fontfamily="sans-serif")

    refs_left = [
        ("DPR", "Karpukhin et al., 2020", "Dense Passage Retrieval — pioneered\ndual-encoder neural retrieval"),
        ("ColBERT", "Khattab & Zaharia, 2020", "Late-interaction retrieval with\ntoken-level matching"),
        ("E5", "Wang et al., 2022", "Unified text embeddings for\ntransfer learning"),
        ("BGE-M3", "Chen et al., 2024", "Multilingual embeddings —\nstrongest open-source baseline"),
    ]
    y = 0.70
    for name, cite, desc in refs_left:
        fig.text(0.06, y, name, fontsize=16, fontweight="bold", color=WHITE,
                 fontfamily="sans-serif")
        fig.text(0.18, y, cite, fontsize=13, color=MUTED, fontfamily="sans-serif")
        fig.text(0.06, y - 0.045, desc, fontsize=11, color=MUTED,
                 fontfamily="sans-serif", linespacing=1.3)
        y -= 0.13

    # Right column: Benchmarks
    fig.text(0.55, 0.77, "Evaluation Benchmarks", fontsize=20, fontweight="bold",
             color=ACCENT, fontfamily="sans-serif")

    fig.text(0.55, 0.69, "MTEB", fontsize=16, fontweight="bold", color=WHITE,
             fontfamily="sans-serif")
    fig.text(0.67, 0.69, "Muennighoff et al., 2023", fontsize=13, color=MUTED,
             fontfamily="sans-serif")
    fig.text(0.55, 0.645, "Massive Text Embedding Benchmark\n58 datasets across 8 tasks",
             fontsize=11, color=MUTED, fontfamily="sans-serif", linespacing=1.3)

    fig.text(0.55, 0.56, "BEIR", fontsize=16, fontweight="bold", color=WHITE,
             fontfamily="sans-serif")
    fig.text(0.67, 0.56, "Thakur et al., 2021", fontsize=13, color=MUTED,
             fontfamily="sans-serif")
    fig.text(0.55, 0.515, "Heterogeneous retrieval benchmark\n18 diverse retrieval datasets",
             fontsize=11, color=MUTED, fontfamily="sans-serif", linespacing=1.3)

    # Comparison callout card
    ax_card = fig.add_axes([0.55, 0.14, 0.40, 0.28])
    ax_card.set_facecolor(BG_CARD); ax_card.set_xticks([]); ax_card.set_yticks([])
    for s in ax_card.spines.values(): s.set_color(GOLD); s.set_linewidth(1.5)

    fig.text(0.57, 0.38, "doany-ret-v3 vs. Published", fontsize=16,
             fontweight="bold", color=GOLD, fontfamily="sans-serif")
    fig.text(0.57, 0.32, "Outperforms all baselines on our\ninternal eval suite by 5.8pp+",
             fontsize=14, color=WHITE, fontfamily="sans-serif", linespacing=1.4)
    fig.text(0.57, 0.22, "Competitive with MTEB/BEIR top models\nat significantly lower latency",
             fontsize=13, color=MUTED, fontfamily="sans-serif", linespacing=1.4)

    add_footer(fig, 6)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 7 — Impact & Next Steps
# ══════════════════════════════════════════════════════════════════════
def slide_impact():
    fig = new_slide()
    fig.text(0.06, 0.88, "Impact & Next Steps", fontsize=36, fontweight="bold",
             color=WHITE, fontfamily="sans-serif")
    ax_line = fig.add_axes([0.06, 0.855, 0.24, 0.003])
    ax_line.set_facecolor(ACCENT); ax_line.set_xticks([]); ax_line.set_yticks([])
    for s in ax_line.spines.values(): s.set_visible(False)

    # Three action cards
    cards = [
        ("1", "Update Production Benchmarks",
         "Replace all references to previous\nnumbers with corrected results",
         "Immediate"),
        ("2", "Rerun Q1 Ablation Study",
         "Repeat the ablation with the fixed\nindexing pipeline for accurate deltas",
         "This sprint"),
        ("3", "Publish Corrected Results",
         "Consider updating external-facing\nbenchmark documentation",
         "Next review"),
    ]

    x = 0.06
    for num, title, desc, timeline in cards:
        # Card background
        ax_card = fig.add_axes([x, 0.22, 0.27, 0.55])
        ax_card.set_facecolor(BG_CARD)
        ax_card.set_xticks([]); ax_card.set_yticks([])
        for s in ax_card.spines.values(): s.set_color(BG_CARD); s.set_linewidth(0)

        # Number circle accent
        fig.text(x + 0.025, 0.71, num, fontsize=32, fontweight="bold",
                 color=ACCENT, fontfamily="sans-serif")

        fig.text(x + 0.025, 0.62, title, fontsize=18, fontweight="bold",
                 color=WHITE, fontfamily="sans-serif")
        fig.text(x + 0.025, 0.48, desc, fontsize=14, color=MUTED,
                 fontfamily="sans-serif", linespacing=1.5)

        # Timeline badge
        fig.text(x + 0.025, 0.32, timeline, fontsize=13, fontweight="bold",
                 color=GOLD, fontfamily="sans-serif",
                 bbox=dict(boxstyle="round,pad=0.3", facecolor=BG, edgecolor=GOLD,
                           linewidth=1))
        x += 0.31

    add_footer(fig, 7)
    return fig

# ══════════════════════════════════════════════════════════════════════
# SLIDE 8 — Q&A
# ══════════════════════════════════════════════════════════════════════
def slide_qa():
    fig = new_slide()

    fig.text(0.5, 0.58, "Questions?", fontsize=56, fontweight="bold",
             color=WHITE, fontfamily="sans-serif", ha="center", va="center")
    fig.text(0.5, 0.44, "doany Retrieval Team  |  April 2026",
             fontsize=20, color=MUTED, fontfamily="sans-serif", ha="center")

    # Accent lines
    ax_top = fig.add_axes([0.35, 0.68, 0.30, 0.003])
    ax_top.set_facecolor(ACCENT); ax_top.set_xticks([]); ax_top.set_yticks([])
    for s in ax_top.spines.values(): s.set_visible(False)

    ax_bot = fig.add_axes([0.40, 0.38, 0.20, 0.003])
    ax_bot.set_facecolor(ACCENT); ax_bot.set_xticks([]); ax_bot.set_yticks([])
    for s in ax_bot.spines.values(): s.set_visible(False)

    add_footer(fig, 8)
    return fig

# ══════════════════════════════════════════════════════════════════════
# BUILD PDF
# ══════════════════════════════════════════════════════════════════════
def main():
    builders = [
        slide_title,
        slide_why,
        slide_headline,
        slide_recall,
        slide_latency,
        slide_related,
        slide_impact,
        slide_qa,
    ]

    print(f"Building {len(builders)}-slide PDF → {OUT_PDF.name}")
    with PdfPages(str(OUT_PDF)) as pdf:
        for i, build in enumerate(builders, 1):
            print(f"  Slide {i}/{len(builders)}: {build.__name__}")
            fig = build()
            pdf.savefig(fig, facecolor=fig.get_facecolor(), dpi=200)
            plt.close(fig)

    size_mb = OUT_PDF.stat().st_size / (1024 * 1024)
    print(f"\nDone! {OUT_PDF} ({size_mb:.1f} MB)")


if __name__ == "__main__":
    main()
