#!/usr/bin/env python3
"""記事の図版(SVG)を明暗2枚ずつ作る。標準ライブラリだけで動く。"""
import os

HERE = os.path.dirname(os.path.abspath(__file__))
THEMES = {
    "light": dict(bg="#f5f8f7", ink="#183044", sub="#4e6574", card="#ffffff", line="#c9d6dc",
                  go="#2f8f6b", go_bg="#e3f3ec", maybe="#b7791f", maybe_bg="#fbf1dc", no="#b4443f", no_bg="#f8e4e2",
                  blue="#2b7fae", blue_bg="#e0eff7"),
    "dark": dict(bg="#111e29", ink="#f3f6fb", sub="#c0cfda", card="#18293a", line="#34495a",
                 go="#6fd0a8", go_bg="#16352d", maybe="#e6b95a", maybe_bg="#3a2f17", no="#f08a84", no_bg="#3c1f21",
                 blue="#6fb6d9", blue_bg="#16334a"),
}
FONT = "Hiragino Sans, Noto Sans JP, sans-serif"


def esc(s):
    return s.replace("&", "&amp;").replace("<", "&lt;")


def text(x, y, s, size=13, fill="ink", weight=400, anchor="start", t=None):
    return (f'<text x="{x}" y="{y}" font-size="{size}" fill="{t[fill]}" font-weight="{weight}" '
            f'text-anchor="{anchor}">{esc(s)}</text>')


def wrap(title, desc, w, h, body, t):
    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">{esc(title)}</title>\n<desc id="desc">{esc(desc)}</desc>\n'
            f'<rect width="{w}" height="{h}" rx="24" fill="{t["bg"]}"/>\n<g font-family="{FONT}">\n{body}\n</g>\n</svg>\n')


def fig_triage(t):
    cols = [
        ("go", "やる（根拠が強い）", [
            "AI検索のボットを", "止めていないか確認", "（robots.txtとCDN）",
            None,
            "SEOの土台を点検", "（インデックス・スニペット・", "本文がHTMLにある）",
            None,
            "既存の大事なページに", "独自の一次情報を足す",
            None,
            "計測の窓を開く", "（GA4・Search Console・Bing）",
        ]),
        ("maybe", "足してもいい（効果は未確認）", [
            "Bing Webmaster Toolsに", "登録する",
            None,
            "結論先出し・見出し・表で", "読みやすく整える",
            None,
            "自然な形で外部に", "言及される（比較・レビュー）",
            None,
            "動画・画像を添える",
        ]),
        ("no", "やらなくていい／やらない", [
            "llms.txtを作る", "（Googleは使わない）",
            None,
            "記事を細かく刻む", "「チャンク化」",
            None,
            "言及を集める工作・", "AI向けだけの書き換え",
            None,
            "質問ごとのページ量産", "（スパム扱いになりうる）",
            None,
            "スキーマ・FAQに頼る",
        ]),
    ]
    w, h = 736, 400
    body = [text(30, 42, "AEO（LLMO）対策の仕分け：何をやって、何をやらないか", 20, "ink", 700, t=t),
            text(30, 66, "Google検索については公式ガイドに、それ以外のAIは調査・実測に基づく（2026年10月8日時点）", 12, "sub", t=t)]
    cw, gap, x0, y0 = 220, 13, 30, 90
    for i, (kind, head, lines) in enumerate(cols):
        x = x0 + i * (cw + gap)
        body.append(f'<rect x="{x}" y="{y0}" width="{cw}" height="{h-y0-24}" rx="14" fill="{t[kind+"_bg"]}" stroke="{t[kind]}" stroke-width="1.5"/>')
        body.append(text(x + 14, y0 + 28, head, 13, kind, 700, t=t))
        y = y0 + 62
        for ln in lines:
            if ln is None:
                y += 12
                continue
            body.append(text(x + 14, y, ln, 12.5, "ink", 400, t=t))
            y += 20
    return wrap("AEO（LLMO）対策の仕分け", "やる（AIボットの許可確認、SEOの土台、独自の一次情報、計測）、やってもいい（Bing登録、読みやすい構成、自然な言及、画像動画）、やらなくていい（llms.txt、チャンク化、言及集め、量産、スキーマ頼み）の3列。", w, h, "\n".join(body), t)


def fig_gates(t):
    w, h = 736, 360
    body = [text(30, 42, "AIに読まれる門は2つある。robots.txtだけ見ても足りない", 20, "ink", 700, t=t),
            text(30, 66, "robots.txtは「お願い」の紙。実際に門を閉められるのはCDN/WAF（サーバーの手前の番人）", 12, "sub", t=t)]
    # bot
    def box(x, y, bw, bh, fill, stroke, lines, head=None):
        out = [f'<rect x="{x}" y="{y}" width="{bw}" height="{bh}" rx="12" fill="{t[fill]}" stroke="{t[stroke]}" stroke-width="1.5"/>']
        yy = y + 26
        if head:
            out.append(text(x + bw / 2, yy, head, 13, stroke, 700, "middle", t))
            yy += 22
        for ln in lines:
            out.append(text(x + bw / 2, yy, ln, 12, "ink", 400, "middle", t))
            yy += 19
        return out
    body += box(30, 110, 130, 120, "blue_bg", "blue", ["OAI-SearchBot", "Claude-SearchBot", "PerplexityBot"], "AIの検索ボット")
    body += box(210, 110, 150, 120, "card", "maybe", ["「入ってOK？」を", "読んで自主的に従う", "（強制力はない）"], "門1：robots.txt")
    body += box(410, 110, 150, 120, "card", "no", ["IP・UA・頻度で", "実際に門前払いできる", "（Cloudflare・Akamai等）"], "門2：CDN / WAF")
    body += box(610, 110, 96, 120, "go_bg", "go", ["本文を読む", "→ 引用候補に"], "サイト")
    for x1, x2 in [(160, 210), (360, 410), (560, 610)]:
        body.append(f'<line x1="{x1+2}" y1="170" x2="{x2-6}" y2="170" stroke="{t["sub"]}" stroke-width="2"/>')
        body.append(f'<path d="M{x2-6} 163 L{x2+2} 170 L{x2-6} 177 Z" fill="{t["sub"]}"/>')
    body.append(text(30, 270, "よくある落とし穴", 13, "ink", 700, t=t))
    body.append(text(30, 294, "・robots.txtは「許可」なのに、CDNがボットのUAを門前払いしている（今回の実測で見つかった）", 12.5, "ink", t=t))
    body.append(text(30, 318, "・CDNの遮断が生きていると、robots.txtで許可してもボットは本文に届かない", 12.5, "ink", t=t))
    body.append(text(30, 342, "・robots.txtの変更は、反映まで約24時間かかる（OpenAIの説明）", 12.5, "ink", t=t))
    return wrap("AIに読まれる2つの門", "AIの検索ボットは、robots.txtという自主ルールの門と、CDN/WAFという実際の門の2つを通ってサイトに届く。", w, h, "\n".join(body), t)


for name, fn in [("fig-triage", fig_triage), ("fig-gates", fig_gates)]:
    for mode, suffix in [("light", ""), ("dark", ".dark")]:
        open(os.path.join(HERE, f"{name}{suffix}.svg"), "w", encoding="utf8").write(fn(THEMES[mode]))
print("ok")
