"""図版を書き出す: python3 make_figures.py（明暗2版。数字は data.json から計算する）"""
import json
from collections import Counter
from datetime import datetime, timedelta, timezone
from html import escape
from pathlib import Path

ROOT = Path(__file__).resolve().parent
FONT = "Hiragino Sans, Noto Sans JP, sans-serif"
JST = timezone(timedelta(hours=9))
D = json.loads((ROOT / "data.json").read_text())

# 発言の種類を図では5つにまとめる
GROUPS = {
    "依頼": "依頼",
    "修正": "修正・確認",
    "確認": "修正・確認",
    "方針": "方針・承認",
    "承認": "方針・承認",
    "仕組みづくり": "仕組みづくり",
    "復旧": "復旧・対象外",
    "対象外": "復旧・対象外",
}
ORDER = ["依頼", "修正・確認", "方針・承認", "仕組みづくり", "復旧・対象外"]


def palette(dark):
    keys = "bg panel ink muted line accent soft".split()
    vals = (
        ("#111e29", "#1c2d3c", "#f3f6fb", "#c0cfda", "#839bae", "#97dec5", "#193e37")
        if dark
        else ("#f5f8f7", "#ffffff", "#183044", "#4e6574", "#647d8a", "#17654f", "#e3f2eb")
    )
    c = dict(zip(keys, vals))
    series = ("#7fb2e5", "#e6b566", "#c79be0", "#97dec5", "#7d8f9c") if dark else ("#3b78b8", "#b9791a", "#8a55b0", "#17654f", "#93a3ad")
    c["series"] = dict(zip(ORDER, series))
    labels = (c["bg"],) * 5 if dark else ("#ffffff", "#000000", "#ffffff", "#ffffff", "#183044")
    c["series_text"] = dict(zip(ORDER, labels))
    return c


def t(x, y, s, size=18, color=None, bold=False, anchor="start"):
    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 box(x, y, w, h, fill, stroke, rx=14):
    return f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{rx}" fill="{fill}" stroke="{stroke}" stroke-width="1.5"/>'


def bar(x, y, w, h, fill):
    return f'<rect x="{x:.1f}" y="{y:.1f}" width="{w:.1f}" height="{h:.1f}" fill="{fill}"/>'


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


def day(s):
    return datetime.fromisoformat(s.replace("Z", "+00:00")).astimezone(JST).strftime("%m-%d")


def stages(dark):
    c = palette(dark)
    W, H = 720, 400
    rows = [
        ("1", "ハーネスなし", "人がふつうに仕事をする", ""),
        ("2", "人がハーネスを操作する", "人とエージェントが組んで作業する", "9/28〜9/30のぼくら"),
        ("3", "人がハーネスを束ねる", "人が起動し、出てきたものを確認する", "10/1のぼくら（判定はここ）"),
        ("4", "ハーネスが人を束ねる", "エージェントが自分から動き、人は抜き取りで確認する", "まだ。芽が2つあるだけ"),
    ]
    o = [t(30, 44, "元記事の4段階と、ぼくらの現在地", 21, c["ink"], True)]
    for i, (n, name, sub, mark) in enumerate(rows):
        y = 68 + i * 80
        here = i == 2
        o += [
            box(30, y, 660, 66, c["soft"] if here else c["panel"], c["accent"] if here else c["line"]),
            f'<circle cx="68" cy="{y + 33}" r="19" fill="{c["accent"] if here else c["bg"]}" stroke="{c["line"]}" stroke-width="1.5"/>',
            t(68, y + 40, n, 19, c["bg"] if here else c["muted"], True, "middle"),
            t(102, y + 29, name, 18, c["ink"], True),
            t(102, y + 52, sub, 14, c["muted"]),
        ]
        if mark:
            o.append(t(672, y + 29, mark, 14, c["accent"] if i >= 2 else c["muted"], i == 2, "end"))
    return svg(W, H, "元記事の4段階と、ぼくらの現在地", "段階1はハーネスなし、段階2は人がハーネスを操作、段階3は人がハーネスを束ねる、段階4はハーネスが人を束ねる。このブログの制作は段階3と判定した。", o, c)


def daily(dark):
    c = palette(dark)
    days = sorted({day(m["at"]) for m in D["human_messages"]})
    W, H = 720, 150 + 64 * len(days)
    arts = D["articles_added_by_day_jst"]
    o = [
        t(30, 44, "人の発言の中身と、その日にできた記事の数", 21, c["ink"], True),
        t(30, 68, "日本時間。棒は人の発言の件数、右の数字は追加された記事の本数", 14, c["muted"]),
    ]
    x = 30
    for g in ORDER:
        o += [bar(x, 88, 14, 14, c["series"][g]), t(x + 20, 100, g, 14, c["ink"])]
        x += 34 + len(g) * 14
    scale = 13
    for i, d in enumerate(days):
        y = 128 + i * 64
        counts = Counter(GROUPS[m["label"]] for m in D["human_messages"] if day(m["at"]) == d)
        total = sum(counts.values())
        o.append(t(30, y + 26, f"{int(d[:2])}/{int(d[3:])}", 17, c["ink"], True))
        bx = 100
        for g in ORDER:
            w = counts[g] * scale
            if w:
                o.append(bar(bx, y, w - 1.5, 38, c["series"][g]))
                if counts[g] >= 2:
                    o.append(t(bx + w / 2, y + 25, str(counts[g]), 14, c["series_text"][g], True, "middle"))
                bx += w
        o.append(t(bx + 10, y + 25, f"{total}件", 15, c["muted"]))
        n = arts.get("2026-" + d, 0)
        o += [
            box(560, y - 2, 130, 42, c["soft"] if n >= 10 else c["panel"], c["accent"] if n >= 10 else c["line"], 10),
            t(625, y + 26, f"記事 {n}本", 17, c["accent"] if n >= 10 else c["ink"], n >= 10, "middle"),
        ]
    o.append(t(30, H - 22, "10/1は、28件のうち16件が「仕組みづくり」。この日に33本の記事が追加された。", 15, c["ink"]))
    return svg(W, H, "人の発言の中身と、その日にできた記事の数", "9月27日は発言2件で記事0本、28日は17件で1本、29日は12件で2本、30日は11件で3本、10月1日は28件で33本、4日は2件で0本。10月1日は28件のうち16件が仕組みづくりだった。", o, c)


def starters(dark):
    c = palette(dark)
    W, H = 720, 350
    S = D["sessions"]
    keys = [("human", "人が直接起動", "依頼"), ("agent", "エージェントが別のエージェントに依頼", "修正・確認"), ("workflow", "Workflowが起動", "仕組みづくり")]
    n = Counter(s["started_by"] for s in S)
    tools = Counter()
    for s in S:
        tools[s["started_by"]] += s["tool_calls"]
    o = [t(30, 44, "160のSessionを起動したのは誰か", 21, c["ink"], True)]
    for row, (label, vals) in enumerate((("Sessionの数", n), ("ツール呼び出しの数", tools))):
        y = 78 + row * 92
        total = sum(vals.values())
        o.append(t(30, y + 14, f"{label}（合計 {total:,}）", 15, c["muted"]))
        x = 30
        for k, _, g in keys:
            w = vals[k] / total * 660
            o += [bar(x, y + 24, w - 2, 40, c["series"][g]), t(x + w / 2, y + 50, f"{vals[k]:,}", 15, c["series_text"][g], True, "middle")]
            x += w
    for i, (k, name, g) in enumerate(keys):
        x = 30 + (0, 150, 470)[i]
        o += [bar(x, 262, 14, 14, c["series"][g]), t(x + 20, 274, name, 14, c["ink"])]
    o.append(t(30, 306, "人が直接起動したのは36件（22%）。", 14, c["muted"]))
    o.append(t(30, 328, "残りの124件は、人の依頼を受けたエージェントとWorkflowが起動した。", 14, c["muted"]))
    return svg(W, H, "160のSessionを起動したのは誰か", f"Sessionは人が直接起動{n['human']}件、エージェントが依頼{n['agent']}件、Workflowが起動{n['workflow']}件。ツール呼び出しは順に{tools['human']}回、{tools['agent']}回、{tools['workflow']}回。", o, c)


def titles(dark):
    c = palette(dark)
    W, H = 720, 500
    T = D["title_decisions"]
    cols = [("accepted_as_is", "そのまま採用", "仕組みづくり"), ("rewritten", "人が書き直して採用", "方針・承認"), ("rejected", "不採用", "復旧・対象外"), ("sent_back", "全部出し直し", "修正・確認")]
    tot = {k: sum(x[k] for x in T) for k, _, _ in cols}
    o = [
        t(30, 44, "エージェントが出したタイトル案44本を、人はどう判断したか", 20, c["ink"], True),
    ]
    x = 30
    for k, name, g in cols:
        o += [bar(x, 64, 14, 14, c["series"][g]), t(x + 20, 76, f"{name} {tot[k]}", 14, c["ink"])]
        x += 62 + len(name) * 14
    o.append(t(690, 104, "回答までの待ち", 13, c["muted"], False, "end"))
    for i, row in enumerate(T):
        y = 114 + i * 38
        o.append(t(30, y + 20, row["keyword"], 14, c["ink"]))
        bx = 262
        for k, _, g in cols:
            for _ in range(row[k]):
                o.append(f'<rect x="{bx}" y="{y}" width="50" height="28" rx="6" fill="{c["series"][g]}"/>')
                bx += 56
        o.append(t(690, y + 20, f"{row['wait_minutes']:.0f}分", 14, c["muted"], False, "end"))
    wait = sum(r["wait_minutes"] for r in T)
    chars = sum(r["answer_chars"] for r in T)
    o.append(t(30, 478, f"人の回答は9回で合計{chars}字。エージェントが回答を待った時間は合計{wait:.0f}分。", 15, c["ink"]))
    return svg(W, H, "エージェントが出したタイトル案44本を、人はどう判断したか", f"44本のうち、そのまま採用{tot['accepted_as_is']}本、人が書き直して採用{tot['rewritten']}本、不採用{tot['rejected']}本、全部出し直し{tot['sent_back']}本。", o, c)


for name, fn in (("figure-stages", stages), ("figure-daily", daily), ("figure-starters", starters), ("figure-titles", titles)):
    (ROOT / f"{name}.svg").write_text(fn(False) + "\n")
    (ROOT / f"{name}.dark.svg").write_text(fn(True) + "\n")
    print(name)
