#!/usr/bin/env python3
"""Overlay intra-node and inter-node MB2 curves on the same axes."""
from __future__ import annotations

import argparse
import json
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def load(path: Path) -> list[dict]:
    d = json.loads(path.read_text())
    return [s for s in d["summary"] if s["input_tokens"] >= 64]


def main() -> None:
    p = argparse.ArgumentParser()
    p.add_argument("--intra", type=Path, required=True)
    p.add_argument("--inter", type=Path, required=True)
    p.add_argument("--out-time", type=Path, default=Path("figs/mb2_transfer_time_compare.png"))
    p.add_argument("--out-bw",   type=Path, default=Path("figs/mb2_transfer_bw_compare.png"))
    args = p.parse_args()

    intra = load(args.intra)
    inter = load(args.inter)

    def axis_arrays(rows):
        kv = [r["kv_mib"] for r in rows]
        p50 = [r["pure_transfer_ms_p50"] for r in rows]
        mn = [r["pure_transfer_ms_min"] for r in rows]
        mx = [r["pure_transfer_ms_max"] for r in rows]
        bw_p50 = [r["throughput_gbps_p50"] for r in rows]
        bw_max = [r["throughput_gbps_max"] for r in rows]
        return kv, p50, mn, mx, bw_p50, bw_max

    ai_kv, ai_p50, ai_mn, ai_mx, ai_bw_p50, ai_bw_max = axis_arrays(intra)
    bi_kv, bi_p50, bi_mn, bi_mx, bi_bw_p50, bi_bw_max = axis_arrays(inter)

    # ---- transfer time ----
    fig, ax = plt.subplots(figsize=(8.5, 5))
    ax.errorbar(ai_kv, ai_p50,
                yerr=[np.array(ai_p50) - np.array(ai_mn),
                      np.array(ai_mx) - np.array(ai_p50)],
                fmt="o-", color="#1f77b4", lw=2, markersize=7, capsize=4,
                label="intra-node (dash1 GPU 0↔1)")
    ax.errorbar(bi_kv, bi_p50,
                yerr=[np.array(bi_p50) - np.array(bi_mn),
                      np.array(bi_mx) - np.array(bi_p50)],
                fmt="s--", color="#d62728", lw=2, markersize=7, capsize=4,
                label="inter-node (dash1 GPU0 → dash2 GPU0)")
    # ideal 9.7 GB/s reference
    ref_x = np.array(ai_kv)
    ref_y_ms = (ref_x * 1024 * 1024) / (9.7 * 1e9) * 1000
    ax.plot(ref_x, ref_y_ms, "--", color="#888", alpha=0.5,
            label="9.7 GB/s reference")
    ax.axvline(11500, color="#7a1d1d", lw=0.8, ls=":", alpha=0.5)
    ax.text(11500, 0.7, "p99 agentic req\n11.5 GiB",
            fontsize=8, color="#7a1d1d", ha="center")
    ax.set_xscale("log"); ax.set_yscale("log")
    ax.set_xlabel("KV transfer size (MiB)")
    ax.set_ylabel("Pure transfer time (ms, log)")
    ax.set_title("MB2 intra vs inter — Mooncake transfer cost is topology-independent\n"
                 "(both paths go through 200 Gbps RDMA NIC; intra-node does not use NVLink)")
    ax.grid(True, which="both", alpha=0.3)
    ax.legend(loc="upper left", fontsize=9)
    args.out_time.parent.mkdir(parents=True, exist_ok=True)
    fig.tight_layout(); fig.savefig(args.out_time, dpi=150); plt.close(fig)
    print(f"wrote {args.out_time}")

    # ---- bandwidth ----
    fig, ax = plt.subplots(figsize=(8.5, 5))
    ax.plot(ai_kv, ai_bw_p50, "o-", color="#1f77b4", lw=2, markersize=7,
            label="intra p50")
    ax.plot(ai_kv, ai_bw_max, "x--", color="#1f77b4", lw=1.2, markersize=8,
            alpha=0.7, label="intra max")
    ax.plot(bi_kv, bi_bw_p50, "s-", color="#d62728", lw=2, markersize=7,
            label="inter p50")
    ax.plot(bi_kv, bi_bw_max, "+--", color="#d62728", lw=1.2, markersize=8,
            alpha=0.7, label="inter max")
    ax.axhline(9.7, color="#888", ls="--", alpha=0.5,
               label="steady-state ≈ 9.7 GB/s")
    ax.set_xscale("log")
    ax.set_xlabel("KV transfer size (MiB)")
    ax.set_ylabel("Effective bandwidth (GB/s)")
    ax.set_ylim(0, 12)
    ax.set_title("MB2 intra vs inter — bandwidth")
    ax.grid(True, which="both", alpha=0.3)
    ax.legend(loc="lower left", fontsize=9)
    args.out_bw.parent.mkdir(parents=True, exist_ok=True)
    fig.tight_layout(); fig.savefig(args.out_bw, dpi=150); plt.close(fig)
    print(f"wrote {args.out_bw}")


if __name__ == "__main__":
    main()
