"""experiment/results/summary.json から図版を作る。figure-*.svg（明）と figure-*.dark.svg（暗）の2ファイル方式。
使い方: このフォルダで python make_figures.py"""
import json, os
from xml.sax.saxutils import escape

HERE = os.path.dirname(os.path.abspath(__file__))
S = json.load(open(f"{HERE}/experiment/results/summary.json"))
FONT = "Hiragino Sans, Noto Sans JP, sans-serif"


def pal(dark):
    if dark:
        return dict(bg="#111e29", card="#1c2d3c", ink="#f3f6fb", sub="#c0cfda", line="#839bae", ok="#97dec5", okbg="#193e37", ng="#e6b566", ngbg="#3a2f1b", mid="#2a3c4d", bar="#6fb6d9", acc="#2b4a63")
    return dict(bg="#f5f8f7", card="#ffffff", ink="#183044", sub="#4e6574", line="#647d8a", ok="#17654f", okbg="#e3f2eb", ng="#9a5a00", ngbg="#fbefd9", mid="#e8eef2", bar="#2f7ea6", acc="#dbeaf3")


def t(x, y, s, size, color, anchor="start", bold=False):
    w = ' font-weight="700"' if bold else ""
    return f'<text x="{x}" y="{y}" font-size="{size}" fill="{color}"{w} text-anchor="{anchor}">{escape(s)}</text>'


def svg(w, h, title, desc, body, c):
    return (f'<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" viewBox="0 0 {w} {h}" role="img" aria-labelledby="title desc">\n'
            f'<title id="title">{escape(title)}</title>\n<desc id="desc">{escape(desc)}</desc>\n'
            f'<rect width="{w}" height="{h}" rx="24" fill="{c["bg"]}"/>\n<g font-family="{FONT}">\n' + "\n".join(body) + "\n</g>\n</svg>\n")


def flow(dark):
    c = pal(dark)
    b = [t(30, 40, "構成・執筆・点検・書き直しを分けて、点検は別に頼む", 20, c["ink"], bold=True)]
    bw, bh, gx = 200, 112, 48
    xs = [30, 30 + bw + gx, 30 + 2 * (bw + gx)]
    ys = [74, 244]
    steps = [
        ("① 人が材料を出す", ["読者・約束・一次情報メモ", "事実と出典（推測で埋めない）"], False),
        ("② 構成だけ作る", ["本文は書かせない", "どのメモをどこで使うか決める"], False),
        ("③ 初稿を書く", ["メモにあることだけ書く", "足りない所は【要確認】で空ける"], False),
        ("④ 点検する", ["別の呼び出し（できれば別のAI）", "資料＋観点を渡し、指摘だけさせる"], True),
        ("⑤ 指摘に沿って書き直す", ["指摘が具体的なほど、直る", "資料にないものは足さない"], False),
        ("⑥ 人が最後に確認する", ["事実表で原典を照合", "音読して引っかかる所を直す"], False),
    ]
    pos = []
    for i, (h, lines, hot) in enumerate(steps):
        x = xs[i % 3]; y = ys[i // 3]; pos.append((x, y))
        fill = c["acc"] if hot else c["card"]
        stroke = c["bar"] if hot else c["line"]
        sw = 3 if hot else 1.2
        b.append(f'<rect x="{x}" y="{y}" width="{bw}" height="{bh}" rx="14" fill="{fill}" stroke="{stroke}" stroke-width="{sw}"/>')
        b.append(t(x + 16, y + 30, h, 15, c["ink"], bold=True))
        for j, ln in enumerate(lines):
            b.append(t(x + 16, y + 58 + j * 22, ln, 11.5, c["sub"]))
    def arrow(x1, y1, x2, y2):
        return (f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{c["line"]}" stroke-width="2"/>'
                f'<polygon points="{x2},{y2} {x2-8},{y2-5} {x2-8},{y2+5}" fill="{c["line"]}"/>')
    for r in range(2):
        for k in range(2):
            x1 = xs[k] + bw; x2 = xs[k + 1]; y = ys[r] + bh / 2
            b.append(arrow(x1 + 2, y, x2 - 2, y))
    # ③ → ④ の折れ線
    x3 = xs[2] + bw / 2; y3 = ys[0] + bh; ym = (ys[0] + bh + ys[1]) / 2; x4 = xs[0] + bw / 2
    b.append(f'<polyline points="{x3},{y3+2} {x3},{ym} {x4},{ym} {x4},{ys[1]-8}" fill="none" stroke="{c["line"]}" stroke-width="2"/>')
    b.append(f'<polygon points="{x4},{ys[1]-2} {x4-5},{ys[1]-10} {x4+5},{ys[1]-10}" fill="{c["line"]}"/>')
    b.append(t(30, 394, "④は、書き手とは別に頼む（新しいチャットで、できれば別のAIで）。", 12.5, c["ink"], bold=True))
    b.append(t(30, 414, "①は、実際にやった人の仕事。ここが空だと、実験ではAIが体験も数字も作った。", 12.5, c["sub"]))
    return svg(736, 440, "記事づくりの6工程", "人が材料を出し、構成、初稿、点検、書き直しの順に進める。点検だけは書き手とは別の呼び出しで行い、最後に人が事実表と音読で確認する。", b, c)


COLS = [("年",), ("モデル", "名"), ("数字",), ("結論の", "逆転"), ("集計", "範囲"), ("順位",), ("期間",), ("資料に", "ない因果"), ("出典の", "人物"), ("作り話", "の体験")]


def heat(dark):
    c = pal(dark); H = S["hard"]
    rows = [
        ("直す", None),
        ("「もっと良くして」（資料なし）", ("a|rewrite_plain|claude-opus-5-5", "fix")),
        ("「もっと良くして」（資料あり）", ("a|rewrite_facts|claude-opus-5-5", "fix")),
        ("点検の指摘で書き直し（資料＋観点）", ("a2|review_check|claude-opus-5-5", "fix")),
        ("指摘する（点検だけ頼む）", None),
        ("資料なし・観点なし（Opus 5.5）", ("a|review_plain|claude-opus-5-5", "hit")),
        ("資料あり・観点なし（Opus 5.5）", ("a|review_facts|claude-opus-5-5", "hit")),
        ("資料＋観点（Haiku 4.5）", ("a|review_check|claude-haiku-4-5-20251001", "hit")),
        ("資料＋観点（Sonnet 5.5）", ("a|review_check|claude-sonnet-5-5", "hit")),
        ("資料＋観点（Opus 5.5）", ("a|review_check|claude-opus-5-5", "hit")),
        ("資料＋観点（GPT-6 Astra）", ("a|review_check|gpt-6-astra", "hit")),
    ]
    x0, cw, gap = 262, 40, 3
    b = [t(30, 40, "仕込んだ欠陥10個を、どの頼み方で直せたか・見つけられたか", 20, c["ink"], bold=True)]
    for i, lab in enumerate(COLS):
        for j, line in enumerate(lab):
            b.append(t(x0 + i * (cw + gap) + cw / 2, 70 + j * 13, line, 10, c["sub"], "middle"))
    y = 100
    for lab, key in rows:
        if key is None:
            y += 6
            b.append(t(30, y + 14, lab, 12.5, c["bar"], bold=True)); y += 24
            continue
        k, mode = key; r = H[k]; n = r["n"]
        b.append(t(30, y + 21, lab, 12, c["ink"]))
        for i in range(10):
            rem = r["per_defect"][f"D{i+1}"]
            hit = n - rem if mode == "fix" else rem
            if hit == n: fill, ink = c["okbg"], c["ok"]
            elif hit == 0: fill, ink = c["ngbg"], c["ng"]
            else: fill, ink = c["mid"], c["ink"]
            x = x0 + i * (cw + gap)
            b.append(f'<rect x="{x}" y="{y}" width="{cw}" height="30" rx="6" fill="{fill}"/>' + t(x + cw / 2, y + 20, f"{hit}/{n}", 11.5, ink, "middle", True))
        y += 34
    b.append(t(30, y + 20, "緑＝8回とも直った／見つけた、琥珀＝1回も。点検役は書き手とは別の呼び出し。", 11.5, c["sub"]))
    b.append(t(30, y + 38, "書き直しの行は、書き直した本文にその欠陥が残っていなかった回数。", 11.5, c["sub"]))
    return svg(736, y + 60, "欠陥10個の直り方・見つかり方", "資料なしで「もっと良くして」と頼むと欠陥はほとんど残る。資料を渡すと作り話の体験談以外は直る。別の呼び出しで資料と観点を渡して点検させると、3モデルとも10個すべてを指摘した。", b, c)


def fab(dark):
    c = pal(dark); F = S["fabrication"]
    rows = [("draft", "AIの初稿（資料なしで体験談を書かせた）"), ("self_plain", "同じ会話で「見直して」"), ("self_crit", "同じ会話で観点つきで点検して直す"),
            ("twostep_same", "同じ会話で、先に指摘を書いてから直す"), ("separate", "別の呼び出し（同じClaude）が指摘→直す"), ("separate_gpt", "別のAI（GPT-6 Astra）が指摘→直す")]
    b = [t(30, 40, "資料なしで書いた体験談に、作り話は何個残ったか", 20, c["ink"], bold=True),
         t(30, 62, "1本あたりの平均。「書き手が体験した」と主張する記述を数えた（8本ずつ）", 12, c["sub"])]
    x0, W, y = 290, 380, 90
    mx = 14
    for k, lab in rows:
        v = F[k]["mean_claims"]
        b.append(t(30, y + 22, lab, 12, c["ink"]))
        color = c["ok"] if v < 1 else (c["bar"] if k != "draft" else c["ng"])
        b.append(f'<rect x="{x0}" y="{y+4}" width="{max(3, W*v/mx):.0f}" height="26" rx="5" fill="{color}"/>')
        b.append(t(x0 + max(3, W * v / mx) + 8, y + 23, f"{v}個", 13, c["ink"], bold=True))
        y += 40
    y += 4
    b.append(t(30, y + 14, "別のAIの指摘では、体験談を「仮の例」や「想定の整理」に書き換える直し方が目立った。", 11.5, c["sub"]))
    b.append(t(30, y + 32, "同じClaudeの指摘では、【要確認】で空ける直し方が中心だった（平均12.5個）。", 11.5, c["sub"]))
    return svg(736, y + 54, "作り話の残り方", "資料なしで書いた体験談の作り話は、同じ会話での見直しではほとんど減らず、別のAIが指摘してから直すと、ほぼなくなった。", b, c)


for name, fn in [("figure-flow", flow), ("figure-heat", heat), ("figure-fab", fab)]:
    for dark in (False, True):
        open(f"{HERE}/{name}{'.dark' if dark else ''}.svg", "w").write(fn(dark))
