#!/usr/bin/env python3
"""把调研仓库的全部 Markdown 合并为一个可阅读的单页 HTML(report.html)。

用法:python build_report.py
输出:report.html(仓库根目录,自包含,无外部依赖)
"""
import re
import pathlib
import markdown

ROOT = pathlib.Path(__file__).resolve().parent

FILES = [
    ("readme",   "README.md",                         "主报告"),
    ("deepdive", "research/deepdive.md",              "深挖笔记(第 2 轮)"),
    ("evidence", "research/evidence.md",              "证据汇编"),
    ("raw-01",   "research/raw/01-china-market.md",   "01 中国市场"),
    ("raw-02",   "research/raw/02-global-saas.md",    "02 全球产品"),
    ("raw-03",   "research/raw/03-ai-services.md",    "03 服务咨询"),
    ("raw-04",   "research/raw/04-content-audience.md", "04 内容受众"),
    ("raw-05",   "research/raw/05-service-niches.md", "05 服务细分矩阵"),
    ("raw-06",   "research/raw/06-product-ideas.md",  "06 选题库"),
    ("raw-07",   "research/raw/07-case-playbooks.md", "07 案例库"),
]

# README 中唯一的 mermaid 流程图 → 静态 HTML(离线可读,无 JS 依赖)
FLOW_HTML = """<div class="flow">
<span class="chip">① 选细分行业(1–2 周)</span><span class="arrow">→</span>
<span class="chip">② 做 Demo + 写固定价 offer</span><span class="arrow">→</span>
<span class="chip">③ 30 天内拿到第 1 个付费客户</span><span class="arrow">→</span>
<span class="chip">④ 交付 + 沉淀案例,每周 2 篇内容</span><span class="arrow">→</span>
<span class="chip">⑤ 重复需求模板化 → 微 SaaS</span><span class="arrow">→</span>
<span class="chip">⑥ 订阅收入 / 或按 2.47x 利润倍数出售</span>
</div>"""

STYLE = """
:root{--fg:#1f2328;--muted:#59636e;--border:#d1d9e0;--soft:#f6f8fa;--accent:#0969da;--bg:#f6f8fa}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;font:16px/1.75 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;color:var(--fg);background:var(--bg)}
.layout{display:grid;grid-template-columns:268px minmax(0,1fr);gap:28px;max-width:1320px;margin:0 auto;padding:0 24px 24px}
nav.toc{position:sticky;top:20px;align-self:start;max-height:calc(100vh - 40px);overflow:auto;font-size:13px;line-height:1.55;background:#fff;border:1px solid var(--border);border-radius:10px;padding:14px 16px}
nav.toc a{color:var(--fg);text-decoration:none;display:block;padding:2px 0}
nav.toc a:hover{color:var(--accent)}
nav.toc .grp{font-weight:700;color:var(--muted);font-size:11.5px;letter-spacing:.06em;margin:12px 0 3px;text-transform:uppercase}
nav.toc .grp a{color:var(--muted)}
nav.toc .ind{padding-left:10px;border-left:2px solid var(--soft)}
nav.toc a.top{font-weight:600;border-bottom:1px solid var(--soft);padding-bottom:8px;margin-bottom:4px}
main{background:#fff;border:1px solid var(--border);border-radius:10px;padding:36px 48px;min-width:0}
main h1{font-size:26px;border-bottom:2px solid var(--border);padding-bottom:12px;margin:64px 0 20px}
main section.doc:first-of-type > h1:first-child{margin-top:0}
main h2{font-size:21px;border-bottom:1px solid var(--border);padding-bottom:8px;margin-top:44px}
main h3{font-size:17px;margin-top:32px}
main h4{font-size:15.5px}
a{color:var(--accent);text-decoration:none;word-break:break-word}
a:hover{text-decoration:underline}
blockquote{margin:16px 0;padding:10px 18px;border-left:4px solid var(--border);background:var(--soft);color:var(--muted);border-radius:0 6px 6px 0}
blockquote table{background:#fff}
code{font-family:ui-monospace,SFMono-Regular,Consolas,"Courier New",monospace;font-size:13.5px;background:var(--soft);padding:2px 5px;border-radius:4px}
pre{background:var(--soft);border:1px solid var(--border);border-radius:6px;padding:14px;overflow:auto}
pre code{background:none;padding:0}
.tw{overflow-x:auto;margin:16px 0}
table{border-collapse:collapse;width:100%;font-size:14px}
th,td{border:1px solid var(--border);padding:8px 10px;text-align:left;vertical-align:top}
th{background:var(--soft);font-weight:600;white-space:nowrap}
tbody tr:nth-child(even){background:#fbfcfe}
hr{border:0;border-top:1px solid var(--border);margin:44px 0}
.doc-header{max-width:1320px;margin:0 auto;padding:30px 24px 18px}
.doc-header h1{font-size:30px;margin:0 0 8px}
.doc-header .meta{color:var(--muted);font-size:13.5px;margin:0 0 10px}
.guide{background:#fff;border:1px solid var(--border);border-radius:10px;padding:12px 18px;font-size:13.5px;color:var(--muted)}
.guide b{color:var(--fg)}
.flow{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:18px 0;font-size:13.5px}
.flow .chip{background:var(--soft);border:1px solid var(--border);border-radius:999px;padding:6px 12px}
.flow .arrow{color:var(--muted)}
footer{max-width:1320px;margin:20px auto 56px;padding:0 24px;color:var(--muted);font-size:12.5px}
section.doc + section.doc h1{margin-top:72px}
@media (max-width:960px){.layout{grid-template-columns:1fr}nav.toc{position:static;max-height:none}main{padding:24px 20px}}
@media print{nav.toc{display:none}.layout{display:block}main{border:0;padding:0}}
"""

md_engine = markdown.Markdown(
    extensions=["tables", "fenced_code", "sane_lists", "toc", "attr_list", "md_in_html"]
)


def collect_tokens():
    out = []

    def walk(items):
        for t in items:
            out.append({
                "level": t["level"],
                "id": t["id"],
                "name": re.sub(r"<[^>]+>", "", t["name"]).strip(),
            })
            walk(t.get("children", []))

    walk(md_engine.toc_tokens)
    return out


def remap_links(h):
    def repl(m):
        href = m.group(1)
        if href.startswith(("http://", "https://", "#", "mailto:")):
            return m.group(0)
        low = href.lower()
        if "readme.md" in low:
            return 'href="#top"'
        if "evidence.md" in low:
            return 'href="#evidence"'
        if "deepdive.md" in low:
            return 'href="#deepdive"'
        if low.rstrip("/").endswith("raw"):
            return 'href="#raw-01"'
        mm = re.search(r"0(\d)[-_]", low)
        if mm:
            return f'href="#raw-0{mm.group(1)}"'
        return m.group(0)

    return re.sub(r'href="([^"]+)"', repl, h)


def wrap_tables(h):
    return re.sub(r"(<table>.*?</table>)", r'<div class="tw">\1</div>', h, flags=re.S)


def build():
    parts, nav = [], []
    for fid, rel, title in FILES:
        text = (ROOT / rel).read_text(encoding="utf-8")
        text = re.sub(r"```mermaid.*?```", FLOW_HTML, text, flags=re.S)
        md_engine.reset()
        body = md_engine.convert(text)
        tokens = collect_tokens()

        # 给标题 id 加文件前缀,避免跨文件重复
        for t in tokens:
            body = body.replace(f'id="{t["id"]}"', f'id="{fid}--{t["id"]}"', 1)

        # 每个文件只保留第一个 h1(文件标题),其余 h1 降级为 h2
        chunks = body.split("<h1")
        if len(chunks) > 2:
            body = chunks[0] + "<h1" + chunks[1] + "".join(
                "<h2" + c.replace("</h1>", "</h2>") for c in chunks[2:]
            )

        body = wrap_tables(remap_links(body))

        if fid == "raw-01":
            nav.append(("grp", "附录 · 原始笔记", "#raw-01", "raw"))
        if fid.startswith("raw-"):
            nav.append(("raw", title, f"#{fid}", fid))
        else:
            nav.append(("grp", title, f'#{fid}--{tokens[0]["id"]}' if tokens else f"#{fid}", fid))
            for t in tokens:
                if t["level"] == 2:
                    nav.append(("h2", t["name"], f'#{fid}--{t["id"]}', fid))

        parts.append(f'<section class="doc" id="{fid}">\n{body}\n</section>')

    nav_html = ['<a class="top" href="#top">▲ 顶部</a>']
    for kind, label, href, fid in nav:
        if kind == "grp":
            nav_html.append(f'<div class="grp"><a href="{href}">{label}</a></div>')
        elif kind == "raw":
            nav_html.append(f'<div class="ind"><a href="{href}">{label}</a></div>')
        else:
            nav_html.append(f'<div class="ind"><a href="{href}">{label}</a></div>')

    html = f"""<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>用 AI 赚钱 · 2026 调研全集</title>
<style>{STYLE}</style>
</head>
<body id="top">
<header class="doc-header">
  <h1>用 AI 赚钱 —— 2026 调研全集</h1>
  <p class="meta">生成日期 2026-10-11 · 共 10 份文档合并 · 全部数字均带可点击来源,原文可回源核验</p>
  <p class="guide"><b>阅读指南:</b>「主报告」= 结论与路线图 · 「深挖笔记」= 第二轮深挖(服务细分选择 / 选题库 / 案例规律)· 「证据汇编」= 全部关键数字与来源索引 · 「附录」= 7 份原始调研笔记(完整来源清单与置信度分级)。</p>
</header>
<div class="layout">
<nav class="toc">
{chr(10).join(nav_html)}
</nav>
<main>
{chr(10).join(parts)}
</main>
</div>
<footer>本页由 build_report.py 从 Markdown 源文件自动生成;外部链接指向原始来源(部分中文站点可能需要直接访问)。浏览器打印(Ctrl+P)可另存为 PDF。</footer>
</body>
</html>
"""
    out = ROOT / "report.html"
    out.write_text(html, encoding="utf-8")
    size = out.stat().st_size
    print(f"OK: {out} ({size/1024:.1f} KB)")
    print(f"sections={len(parts)} nav_items={len(nav_html)-1}")


if __name__ == "__main__":
    build()
