#!/usr/bin/env python3
"""記事「AIっぽい文章の特徴」の図を 2 枚書き出す（背景込み・外部参照なしの SVG。配色は docs/seo/assets/figures の既存図に合わせる）。

  python3 -I make_figures.py --ai texts-v1.jsonl --human human-clean-v1-18.jsonl --out-dir ../../../docs/seo/assets/figures

- aistyle-cv.svg: 人と比べた 5 お題での、1 本ごとの文の長さのばらつき（変動係数）。人は値の点だけを描き、本文は使わない
- aistyle-example.svg: AI の実例 1 本の文の長さの並び（GPT・お題だけ・s02 の 1 回目。平均に近い本から選んだ）
"""
import argparse
import json
import os
import random
import statistics
import csv
from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).resolve().parent))

from ai_style_count import count_text, sentences, strip_ws

TOPICS = ("s01", "s02", "s03", "s04", "s11")
GROUPS = [("human", "人"), ("anthropic", "Claude"), ("openai", "GPT"), ("google", "Gemini")]
EXAMPLE_ID = "s02|openai/gpt-6.1-sol|plain|0"
BG, FG, MUTED, GRAY, CYAN = "#090b12", "#e6e9f0", "#8a93a6", "#3a4150", "#1ec8e8"
JP = 'font-family="Noto Sans JP, sans-serif"'
MONO = 'font-family="DM Mono, ui-monospace, monospace"'


def load(path):
    return [json.loads(line) for line in open(path, encoding="utf-8") if line.strip()]


def group_of(row):
    return "human" if row["model"] == "human" else row["model"].split("/")[0]


def cv_figure(rows):
    x0, x1, lo, hi = 230, 1110, 0.1, 0.8
    sx = lambda v: x0 + (v - lo) / (hi - lo) * (x1 - x0)
    rng = random.Random(20261101)
    out = [f'<!-- 文の長さのばらつき（変動係数）を 1 本ごとに点で並べた図 / 1200×675 / 記事「人の文章と比べると」直下 -->',
           '<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="675" viewBox="0 0 1200 675">',
           f'<rect width="1200" height="675" fill="{BG}"/>',
           f'<text x="{x0}" y="80" fill="{FG}" {JP} font-size="30">文の長さのばらつき（1 本ごと・5 お題）</text>',
           f'<text x="{x0}" y="118" fill="{MUTED}" {JP} font-size="22">右ほど、短い文と長い文が混ざる。縦線は平均</text>']
    for v in (0.2, 0.4, 0.6, 0.8):
        out.append(f'<line x1="{sx(v):.1f}" y1="150" x2="{sx(v):.1f}" y2="590" stroke="{GRAY}" stroke-width="1"/>')
        out.append(f'<text x="{sx(v):.1f}" y="625" fill="{MUTED}" {MONO} font-size="22" text-anchor="middle">{v:.1f}</text>')
    for i, (g, label) in enumerate(GROUPS):
        y = 200 + i * 110
        vals = [(count_text(r["text"], markers="v1.1")["S05_cv_sent_len"] if "text" in r
                 else float(r["S05_cv_sent_len"])) for r in rows if group_of(r) == g]
        color = CYAN if g == "human" else MUTED
        out.append(f'<text x="{x0 - 30}" y="{y + 9}" fill="{FG}" {JP} font-size="26" text-anchor="end">{label}</text>')
        out.append(f'<text x="{x0 - 30}" y="{y + 38}" fill="{MUTED}" {JP} font-size="18" text-anchor="end">{len(vals)} 本</text>')
        for v in vals:
            out.append(f'<circle cx="{sx(v):.1f}" cy="{y + rng.uniform(-22, 22):.1f}" r="7" fill="{color}" fill-opacity="0.55"/>')
        m = statistics.mean(vals)
        out.append(f'<line x1="{sx(m):.1f}" y1="{y - 34}" x2="{sx(m):.1f}" y2="{y + 34}" stroke="{FG}" stroke-width="3"/>')
        out.append(f'<text x="{sx(m) + 10:.1f}" y="{y - 22}" fill="{FG}" {MONO} font-size="22">{m:.2f}</text>')
    out.append("</svg>")
    return "\n".join(out) + "\n"


def example_figure(row):
    lens = [len(strip_ws(s)) for s in sentences(row["text"])]
    x0, x1, base, top = 150, 1110, 560, 190
    step = (x1 - x0) / len(lens)
    sy = lambda v: base - v / 60 * (base - top)
    m = statistics.mean(lens)
    cv = statistics.pstdev(lens) / m
    out = [f'<!-- AI の実例 1 本（{row["id"]}）の文の長さの並び / 1200×675 / 記事「人の文章と比べると」の表の後 -->',
           '<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="675" viewBox="0 0 1200 675">',
           f'<rect width="1200" height="675" fill="{BG}"/>',
           f'<text x="{x0}" y="80" fill="{FG}" {JP} font-size="30">GPT の 1 本：文の長さの並び（{len(lens)} 文）</text>',
           f'<text x="{x0}" y="118" fill="{MUTED}" {JP} font-size="22">平均 {m:.0f} 字・ばらつき（変動係数）{cv:.2f}。お題だけの条件</text>']
    for v in (0, 20, 40, 60):
        out.append(f'<line x1="{x0}" y1="{sy(v):.1f}" x2="{x1}" y2="{sy(v):.1f}" stroke="{GRAY}" stroke-width="1"/>')
        out.append(f'<text x="{x0 - 16}" y="{sy(v) + 8:.1f}" fill="{MUTED}" {MONO} font-size="22" text-anchor="end">{v}</text>')
    for i, n in enumerate(lens):
        out.append(f'<rect x="{x0 + i * step + step * 0.15:.1f}" y="{sy(n):.1f}" width="{step * 0.7:.1f}" height="{base - sy(n):.1f}" rx="3" fill="{CYAN}" fill-opacity="0.8"/>')
    out.append(f'<line x1="{x0}" y1="{sy(m):.1f}" x2="{x1}" y2="{sy(m):.1f}" stroke="{FG}" stroke-width="2" stroke-dasharray="8 6"/>')
    out.append(f'<text x="{x0}" y="610" fill="{MUTED}" {JP} font-size="22">1 文目</text>')
    out.append(f'<text x="{x1}" y="610" fill="{MUTED}" {JP} font-size="22" text-anchor="end">{len(lens)} 文目</text>')
    out.append(f'<text x="{x0 - 16}" y="160" fill="{MUTED}" {JP} font-size="20" text-anchor="end">字</text>')
    out.append("</svg>")
    return "\n".join(out) + "\n"


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--ai", required=True)
    source = ap.add_mutually_exclusive_group(required=True)
    source.add_argument("--human")
    source.add_argument("--human-csv", help="公開用の全文の数値CSV（本文不要）")
    ap.add_argument("--out-dir", required=True)
    a = ap.parse_args()
    ai = load(a.ai)
    if a.human_csv:
        with open(a.human_csv, encoding="utf-8", newline="") as f:
            human = [{**r, "model": "human"} for r in csv.DictReader(f)]
    else:
        human = load(a.human)
    rows = human + [r for r in ai if r["questionId"] in TOPICS]
    os.makedirs(a.out_dir, exist_ok=True)
    with open(os.path.join(a.out_dir, "aistyle-cv.svg"), "w", encoding="utf-8") as f:
        f.write(cv_figure(rows))
    with open(os.path.join(a.out_dir, "aistyle-example.svg"), "w", encoding="utf-8") as f:
        f.write(example_figure(next(r for r in ai if r["id"] == EXAMPLE_ID)))


if __name__ == "__main__":
    main()
