"""図版のSVG（明暗2版）を書き出す。使い方: python make_figures.py
AAの数値は 2026-10-06 に artificialanalysis.ai の比較ページから取得した値（data/aa-rows.json）。
実験の図は data/summary.json から作る（run_experiment.py → summarize.py の出力）。"""
import json, os

HERE = os.path.dirname(os.path.abspath(__file__))
THEMES = {
    "": dict(bg="#ffffff", ink="#1f2937", sub="#6b7280", box="#f3f4f6", edge="#9ca3af", grid="#e5e7eb",
             cl="#d9622b", cx="#2563eb", gk="#059669", line="#4b5563"),
    ".dark": dict(bg="#111827", ink="#e5e7eb", sub="#9ca3af", box="#1f2937", edge="#6b7280", grid="#374151",
                  cl="#fb923c", cx="#60a5fa", gk="#34d399", line="#9ca3af"),
}
AA = json.load(open(os.path.join(HERE, "data", "aa-rows.json")))


def head(w, h, alt, c):
    return (f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}" width="{w}" height="{h}" '
            f'font-family="sans-serif" role="img" aria-label="{alt}">\n<title>{alt}</title>\n'
            f'<rect width="{w}" height="{h}" fill="{c["bg"]}"/>\n')


def t(x, y, s, c, size=13, anchor="start", color="ink", bold=False):
    b = ' font-weight="bold"' if bold else ""
    return f'<text x="{x}" y="{y}" font-size="{size}" text-anchor="{anchor}" fill="{c[color]}"{b}>{s}</text>\n'


def col(c, tool):
    return c[{"claude": "cl", "codex": "cx", "grok": "gk"}[tool]]


NAMES = {"claude": "Claude Code", "codex": "Codex", "grok": "Grok Build"}


def fig01(c):
    """代表行の3枚カード"""
    w, h = 760, 330
    s = head(w, h, "AAの代表行。Claude Codeはスコア68でSonnet 5.5のmax、1タスク87分・14.19ドル。Codexは63でGPT-6.1 Solのxhigh、15.5分・1.04ドル。Grok Buildは56でGrok 4.7のxhigh、39分・8.82ドル", c)
    cards = [("claude", 68, "Sonnet 5.5", "max", "87.4分", "$14.19", "27.7M"),
             ("codex", 63, "GPT-6.1 Sol", "xhigh", "15.5分", "$1.04", "3.2M"),
             ("grok", 56, "Grok 4.7", "xhigh", "39.2分", "$8.82", "14.3M")]
    for i, (k, idx, model, eff, tm, cost, tok) in enumerate(cards):
        x = 20 + i * 245
        s += f'<rect x="{x}" y="20" width="230" height="290" rx="12" fill="{c["box"]}" stroke="{col(c, k)}" stroke-width="2.5"/>\n'
        s += t(x + 115, 50, NAMES[k], c, 16, "middle", bold=True)
        s += t(x + 115, 120, str(idx), c, 64, "middle", bold=True).replace(f'fill="{c["ink"]}"', f'fill="{col(c, k)}"')
        s += t(x + 115, 143, "Coding Agent Index", c, 12, "middle", "sub")
        s += f'<line x1="{x + 20}" y1="160" x2="{x + 210}" y2="160" stroke="{c["grid"]}"/>\n'
        rows = [("測ったモデル", model), ("effort（考える深さ）", eff), ("1タスクの時間", tm), ("1タスクの費用※", cost), ("1タスクのトークン", tok)]
        for j, (a, b) in enumerate(rows):
            y = 185 + j * 25
            s += t(x + 18, y, a, c, 12, color="sub") + t(x + 212, y, b, c, 14, "end", bold=True)
    s += t(380, 326, "※ API従量課金に換算した額。サブスクの料金ではありません。", c, 11, "middle", "sub")
    return s + "</svg>\n"


def fig02(c):
    """3指標の内訳"""
    w, h = 760, 380
    s = head(w, h, "スコアの内訳。DeepSWEは3つとも72〜73%でほぼ同じ。Terminal-Bench 4.0はClaude Code 66%、Codex 55%、Grok Build 33%。SWE-Atlas-QnAは67%、61%、63%", c)
    groups = [("DeepSWE v1.1", "既存コードの修正（113件）", (72, 73, 73)),
              ("Terminal-Bench 4.0", "ターミナル作業（66件）", (66, 55, 33)),
              ("SWE-Atlas-QnA", "コードを読んで答える（124件）", (67, 61, 63))]
    tools = ["claude", "codex", "grok"]
    for gi, (name, sub, vals) in enumerate(groups):
        y0 = 20 + gi * 118
        s += t(20, y0 + 14, name, c, 15, bold=True) + t(20 + 140 if False else 200, y0 + 14, sub, c, 12, color="sub")
        for ti, k in enumerate(tools):
            y = y0 + 26 + ti * 26
            v = vals[ti]
            s += t(110, y + 15, NAMES[k], c, 12, "end")
            s += f'<rect x="120" y="{y}" width="{v * 8}" height="20" rx="3" fill="{col(c, k)}"/>\n'
            s += t(120 + v * 8 + 8, y + 15, f"{v}%", c, 13, bold=True)
    return s + "</svg>\n"


def fig03(c):
    """effortの階段：費用 vs Index"""
    w, h = 760, 420
    s = head(w, h, "effortを上げたときのスコアと費用。Claude Code（Sonnet 5.5）はlowの42点0.48ドルからmaxの68点14.19ドルまで動く。Codex（GPT-6.1 Sol）はlowの57点0.50ドルからxhighの63点1.04ドルで、maxは60点。Grok Build（Grok 4.7 xhigh）は56点8.82ドル", c)
    L, R, T, B = 70, 730, 30, 360
    import math
    xs = lambda v: L + (math.log10(v) - math.log10(0.3)) / (math.log10(20) - math.log10(0.3)) * (R - L)
    ys = lambda v: B - (v - 35) / (72 - 35) * (B - T)
    for v in range(40, 71, 10):
        s += f'<line x1="{L}" y1="{ys(v):.0f}" x2="{R}" y2="{ys(v):.0f}" stroke="{c["grid"]}"/>\n' + t(L - 8, ys(v) + 4, str(v), c, 12, "end", "sub")
    for v in (0.5, 1, 2, 5, 10):
        s += f'<line x1="{xs(v):.0f}" y1="{T}" x2="{xs(v):.0f}" y2="{B}" stroke="{c["grid"]}"/>\n' + t(xs(v), B + 18, f"${v:g}", c, 12, "middle", "sub")
    s += t((L + R) / 2, B + 40, "1タスクあたりの費用（API従量換算、対数軸）→", c, 12, "middle", "sub")
    s += t(18, T + 6, "↑ Index", c, 12, color="sub")
    for k in ("claude", "codex"):
        rows = AA[k]
        pts = [(xs(r["cost"]), ys(r["index"])) for r in rows]
        s += f'<polyline points="{" ".join(f"{x:.0f},{y:.0f}" for x, y in pts)}" fill="none" stroke="{col(c, k)}" stroke-width="2" stroke-dasharray="4 3"/>\n'
        for (x, y), r in zip(pts, rows):
            s += f'<circle cx="{x:.0f}" cy="{y:.0f}" r="5.5" fill="{col(c, k)}"/>\n'
            dx, dy, anc = r.get("lab", (8, -8, "start"))
            s += t(x + dx, y + dy, r["effort"], c, 12, anc, color="ink")
    for r in AA["grok"]:
        x, y = xs(r["cost"]), ys(r["index"])
        s += f'<rect x="{x - 5.5:.0f}" y="{y - 5.5:.0f}" width="11" height="11" fill="{col(c, "grok")}"/>\n'
        dx, dy, anc = r.get("lab", (8, -8, "start"))
        s += t(x + dx, y + dy, r["effort"], c, 12, anc)
    ly = 300
    for i, k in enumerate(("claude", "codex", "grok")):
        lx = 470
        mark = (f'<rect x="{lx - 5}" y="{ly + i * 20 - 5}" width="10" height="10" fill="{col(c, k)}"/>' if k == "grok"
                else f'<circle cx="{lx}" cy="{ly + i * 20}" r="5" fill="{col(c, k)}"/>')
        s += mark + t(lx + 12, ly + i * 20 + 4, {"claude": "Claude Code（Sonnet 5.5）", "codex": "Codex（GPT-6.1 Sol）", "grok": "Grok Build（Grok 4.7 / 4.6）"}[k], c, 12)
    return s + "</svg>\n"


def fig04(c):
    """effort別・指標別（Claude CodeとCodex）"""
    w, h = 760, 330
    s = head(w, h, "effortを上げたときの伸び方。DeepSWEはClaude Codeが62から72、Codexが68から73とほぼ平ら。Terminal-Bench 4.0はClaude Codeが25から66と大きく伸び、Codexは49から55", c)
    panels = [("DeepSWE v1.1", "deepswe", (55, 75)), ("Terminal-Bench 4.0", "tb", (20, 70)), ("SWE-Atlas-QnA", "qna", (35, 70))]
    pw = 225
    for pi, (name, key, (lo, hi)) in enumerate(panels):
        x0 = 45 + pi * 240
        T, B = 60, 250
        ys = lambda v: B - (v - lo) / (hi - lo) * (B - T)
        s += t(x0 + pw / 2, 30, name, c, 14, "middle", bold=True)
        for v in range(lo, hi + 1, 5 if hi - lo <= 20 else 10):
            s += f'<line x1="{x0}" y1="{ys(v):.0f}" x2="{x0 + pw}" y2="{ys(v):.0f}" stroke="{c["grid"]}"/>\n' + t(x0 - 6, ys(v) + 4, str(v), c, 11, "end", "sub")
        for i, e in enumerate(AA["efforts"]):
            xx = x0 + 15 + i * (pw - 30) / 4
            s += t(xx, B + 18, e, c, 11, "middle", "sub")
        for k in ("claude", "codex"):
            pts = [(x0 + 15 + i * (pw - 30) / 4, ys(r[key])) for i, r in enumerate(AA[k])]
            s += f'<polyline points="{" ".join(f"{x:.0f},{y:.0f}" for x, y in pts)}" fill="none" stroke="{col(c, k)}" stroke-width="2.5"/>\n'
            for (x, y), r in zip(pts, AA[k]):
                s += f'<circle cx="{x:.0f}" cy="{y:.0f}" r="4" fill="{col(c, k)}"/>\n'
            s += t(pts[-1][0] + 4, pts[-1][1] + 4 if k == "claude" else pts[-1][1] + 14, f'{AA[k][-1][key]}', c, 11, "end" if False else "end", "sub") if False else ""
    s += f'<circle cx="250" cy="300" r="5" fill="{c["cl"]}"/>' + t(262, 304, "Claude Code（Sonnet 5.5）", c, 12) + f'<circle cx="460" cy="300" r="5" fill="{c["cx"]}"/>' + t(472, 304, "Codex（GPT-6.1 Sol）", c, 12)
    s += t(380, 322, "横軸は左からlow → medium → high → xhigh → max。縦軸の範囲は図ごとに違います（%）", c, 11, "middle", "sub")
    return s + "</svg>\n"


FIGS = {"fig01-cards": fig01, "fig02-breakdown": fig02, "fig03-effort-cost": fig03, "fig04-effort-bench": fig04}
if os.path.exists(os.path.join(HERE, "data", "figs_exp.py")):
    exec(open(os.path.join(HERE, "data", "figs_exp.py")).read())

for suffix, c in THEMES.items():
    for name, fn in FIGS.items():
        open(os.path.join(HERE, f"{name}{suffix}.svg"), "w").write(fn(c))
