#!/usr/bin/env python3
import argparse
import json
import html
import shutil
from pathlib import Path
from typing import Any


MODES = {'academic', 'business', 'pitch'}
LEVELS = {'v2', 'v3', 'v4'}
LEVEL_NOTES = {
    'v2': '基礎交付版：paper/slides/speaker-notes/deck',
    'v3': '洞察強化版：v2 + insights + 每張圖逐頁解讀',
    'v4': '正式交付版：v3 + 更正式 deck 視覺 + PDF-ready 工作流',
}


def read_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding='utf-8'))


def read_text(path: Path) -> str:
    return path.read_text(encoding='utf-8')


def find_plots(analysis_dir: Path) -> list[Path]:
    return sorted([p for p in analysis_dir.glob('*.png') if p.is_file()])


def build_key_findings(summary: dict[str, Any]) -> list[str]:
    findings: list[str] = []
    for name, meta in summary.get('columnProfiles', {}).items():
        if 'mean' in meta and meta.get('mean') is not None:
            findings.append(f"欄位「{name}」平均值約為 {meta['mean']:.2f}，總和約為 {meta['sum']:.2f}。")
        elif meta.get('topValues'):
            top = meta['topValues'][0]
            findings.append(f"欄位「{name}」最常見值為「{top['value']}」，出現 {top['count']} 次。")
        if len(findings) >= 6:
            break
    if not findings:
        findings.append('資料已完成初步整理，但尚缺少足夠特徵以自動歸納具體發現。')
    return findings


def build_method_text(summary: dict[str, Any]) -> str:
    rows = summary.get('rows', 0)
    cols = summary.get('columns', 0)
    parsed_dates = summary.get('parsedDateColumns', [])
    parts = [f"本研究以一份包含 {rows} 筆資料、{cols} 個欄位的資料集作為分析基礎。"]
    if parsed_dates:
        parts.append(f"其中已自動辨識日期欄位：{', '.join(parsed_dates)}。")
    parts.append("分析流程包含欄位剖析、數值摘要、類別分布觀察，以及圖表化初步探索。")
    return ''.join(parts)


def build_limitations(summary: dict[str, Any], mode: str) -> list[str]:
    base = [
        '本版本內容依據自動分析結果生成，仍需依情境補充背景、語境與論證細節。',
        '目前主要反映描述性分析與初步視覺化結果，尚未自動進行嚴格因果推論或完整驗證。',
    ]
    if mode == 'pitch':
        base[0] = '本版本適合作為提案底稿，但對外簡報前仍需補上商業敘事、案例與風險說明。'
    elif mode == 'business':
        base[0] = '本版本可支援內部決策討論，但正式匯報前仍建議補充商務脈絡與對照基準。'
    elif mode == 'academic':
        base[0] = '本版本可作為論文或研究報告草稿，但正式提交前仍需補足文獻回顧、研究問題與方法論細節。'
    if not summary.get('plots'):
        base.append('本次分析未包含圖表產物，因此視覺化證據仍需後續補充。')
    return base


def classify_plot(name: str) -> str:
    low = name.lower()
    if low.startswith('hist_'):
        return 'histogram'
    if low.startswith('bar_'):
        return 'bar'
    if low.startswith('line_'):
        return 'line'
    return 'plot'


def interpret_plot(plot: Path, mode: str) -> dict[str, str]:
    kind = classify_plot(plot.name)
    base = {
        'histogram': {
            'title': f'圖表解讀：{plot.name}',
            'summary': '這張 histogram 用來觀察數值欄位的分布狀態、集中區域與可能的離群位置。',
            'so_what': '若資料分布偏斜或過度集中，後續可考慮分群、分層或補充異常值檢查。',
        },
        'bar': {
            'title': f'圖表解讀：{plot.name}',
            'summary': '這張 bar chart 適合比較不同類別或分組之間的量體差異，幫助快速辨識高低落差。',
            'so_what': '若類別差異明顯，後續可針對高表現或低表現組別追查原因與策略。',
        },
        'line': {
            'title': f'圖表解讀：{plot.name}',
            'summary': '這張 line chart 用於觀察時間序列變化，幫助辨識趨勢、波動與可能轉折點。',
            'so_what': '若趨勢持續上升或下降，建議進一步比對外部事件、季節性與干預因素。',
        },
        'plot': {
            'title': f'圖表解讀：{plot.name}',
            'summary': '這張圖表提供一個視覺化切面，有助於快速掌握資料重點與分布特徵。',
            'so_what': '建議將圖表與主要論點對齊，補上更具體的背景解讀。',
        },
    }[kind]

    if mode == 'pitch':
        base['so_what'] = '簡報時應直接說明這張圖支持了哪個主張，以及它如何增加說服力。'
    elif mode == 'business':
        base['so_what'] = '建議把這張圖對應到 KPI、風險或下一步行動，方便管理層做判斷。'
    elif mode == 'academic':
        base['so_what'] = '建議將這張圖與研究問題、假設或比較基準一起討論，以提升論證完整度。'
    return base


def build_insights(summary: dict[str, Any], plots: list[Path], mode: str) -> list[str]:
    insights: list[str] = []
    numeric = []
    categorical = []
    for name, meta in summary.get('columnProfiles', {}).items():
        if 'mean' in meta and meta.get('mean') is not None:
            numeric.append((name, meta))
        elif meta.get('topValues'):
            categorical.append((name, meta))

    for name, meta in numeric[:3]:
        insights.append(f"數值欄位「{name}」平均約 {meta['mean']:.2f}，範圍約 {meta['min']:.2f} 到 {meta['max']:.2f}。")
    for name, meta in categorical[:2]:
        top = meta['topValues'][0]
        insights.append(f"類別欄位「{name}」目前以「{top['value']}」最常見（{top['count']} 次），值得作為第一輪聚焦對象。")
    if plots:
        insights.append(f"本次已生成 {len(plots)} 張圖表，可直接支撐逐頁圖表解讀與口頭報告。")

    if mode == 'pitch':
        insights.append('對外提案時，建議把最強的一項數據證據前置，讓聽眾先記住價值主張。')
    elif mode == 'business':
        insights.append('內部決策簡報時，建議把洞察轉成 KPI、優先順序與負責人。')
    elif mode == 'academic':
        insights.append('學術/研究情境下，建議將洞察進一步轉成研究問題、比較架構與後續驗證方向。')
    return insights


def make_insights_md(title: str, mode: str, summary: dict[str, Any], plots: list[Path]) -> str:
    insights = build_insights(summary, plots, mode)
    plot_notes = [interpret_plot(p, mode) for p in plots]
    lines = [f"# {title}｜Insights", '', f"- 模式：`{mode}`", '']
    lines.append('## 關鍵洞察')
    lines.extend([f"- {x}" for x in insights])
    lines.append('')
    if plot_notes:
        lines.append('## 圖表解讀摘要')
        for note in plot_notes:
            lines.append(f"### {note['title']}")
            lines.append(f"- 解讀：{note['summary']}")
            lines.append(f"- 延伸：{note['so_what']}")
            lines.append('')
    return '\n'.join(lines).strip() + '\n'


def make_paper(title: str, audience: str, purpose: str, mode: str, level: str, summary: dict[str, Any], report_md: str, plots: list[Path], insights_md: str | None = None) -> str:
    findings = build_key_findings(summary)
    method_text = build_method_text(summary)
    limitations = build_limitations(summary, mode)
    plot_refs = '\n'.join([f"- `{p.name}`" for p in plots]) or '- 無'
    findings_md = '\n'.join([f"- {x}" for x in findings])
    limitations_md = '\n'.join([f"- {x}" for x in limitations])

    if mode == 'academic':
        sections = f"## 摘要\n\n本文面向{audience}，以「{purpose}」為導向，整理目前資料分析結果並形成學術/研究草稿。\n\n## 研究背景與問題意識\n\n本文件根據既有分析產物自動整理，可作為研究報告、論文初稿或研究提案的起點。\n\n## 研究方法\n\n{method_text}\n\n## 研究發現\n\n{findings_md}\n\n## 討論\n\n目前結果可支撐初步描述性討論，後續可進一步補上研究假設、比較對照與方法嚴謹性。\n\n## 限制\n\n{limitations_md}\n\n## 結論\n\n本分析已形成研究性文件的結構基礎，適合進一步擴展為正式研究報告。"
    elif mode == 'business':
        sections = f"## 執行摘要\n\n本文面向{audience}，目的是支援「{purpose}」的商務溝通與內部決策。\n\n## 商務背景\n\n本文件根據既有分析產物自動整理，適合作為內部簡報、策略討論或管理層報告的第一版。\n\n## 分析方法\n\n{method_text}\n\n## 關鍵洞察\n\n{findings_md}\n\n## 商業意涵\n\n目前資料已足以支撐一輪決策討論，建議進一步對照 KPI、目標值與外部環境。\n\n## 風險與限制\n\n{limitations_md}\n\n## 建議下一步\n\n建議針對最具決策價值的指標建立定期追蹤與後續驗證流程。"
    else:
        sections = f"## Pitch Summary\n\n本文面向{audience}，用於支援「{purpose}」的提案、募資或說服型簡報。\n\n## Opportunity\n\n本文件根據既有分析產物自動整理，可作為提案 deck 與口頭簡報的第一版底稿。\n\n## Evidence\n\n{method_text}\n\n## Key Takeaways\n\n{findings_md}\n\n## Why It Matters\n\n目前結果已可形成明確敘事雛形，後續可補上市場機會、競品比較與具體行動方案。\n\n## Risks\n\n{limitations_md}\n\n## Ask / Next Step\n\n建議將數據證據、主張與下一步行動整合成對外一致的提案版本。"

    insight_section = ''
    if insights_md:
        insight_section = f"\n## 洞察摘要\n\n{insights_md}\n"

    return f"# {title}\n\n- 模式：`{mode}`\n- 等級：`{level}` — {LEVEL_NOTES[level]}\n- 對象：{audience}\n- 目的：{purpose}\n\n{sections}\n\n## 圖表與視覺化資產\n\n{plot_refs}{insight_section}\n## 附錄：原始自動分析摘要\n\n{report_md}\n"


def make_slides(title: str, audience: str, purpose: str, mode: str, summary: dict[str, Any], plots: list[Path], level: str) -> str:
    findings = build_key_findings(summary)
    rows = summary.get('rows', 0)
    cols = summary.get('columns', 0)

    if mode == 'academic':
        slides = [
            ('封面', [f'標題：{title}', f'對象：{audience}', f'目的：{purpose}', f'等級：{LEVEL_NOTES[level]}']),
            ('研究問題', ['定義研究背景與核心問題', '說明本次分析欲回答的主題']),
            ('資料概況', [f'資料筆數：{rows}', f'欄位數：{cols}', '已完成基本欄位剖析與摘要']),
            ('方法', ['描述性統計', '類別分布觀察', '視覺化探索']),
            ('研究發現', findings[:3]),
            ('討論', ['解釋主要發現的可能意義', '連結研究問題與資料結果']),
            ('限制', build_limitations(summary, mode)[:2]),
            ('後續研究', ['補充文獻回顧', '加入比較基準與進階分析']),
            ('結論', ['本份簡報可作為研究報告或論文簡報的第一版底稿']),
        ]
    elif mode == 'business':
        slides = [
            ('封面', [f'標題：{title}', f'對象：{audience}', f'目的：{purpose}', f'等級：{LEVEL_NOTES[level]}']),
            ('決策問題', ['這份分析要支援什麼決策', '為什麼現在需要處理']),
            ('資料概況', [f'資料筆數：{rows}', f'欄位數：{cols}', '已完成基本資料盤點']),
            ('分析方法', ['描述性統計', '類別分布觀察', '視覺化探索']),
            ('關鍵洞察', findings[:3]),
            ('商業意涵', ['把數據結果轉成管理層可理解的含義', '指出可能影響的目標或 KPI']),
            ('風險與限制', build_limitations(summary, mode)[:2]),
            ('建議行動', ['列出近期可執行事項', '定義需要追蹤的指標']),
            ('結語', ['本份簡報可作為正式管理簡報的第一版底稿']),
        ]
    else:
        slides = [
            ('封面', [f'標題：{title}', f'對象：{audience}', f'目的：{purpose}', f'等級：{LEVEL_NOTES[level]}']),
            ('痛點 / 機會', ['說明這份分析解決什麼問題', '點出為什麼值得關注']),
            ('證據基礎', [f'資料筆數：{rows}', f'欄位數：{cols}', '已完成資料摘要與圖表探索']),
            ('方法', ['描述性統計', '類別觀察', '關鍵圖表整理']),
            ('核心亮點', findings[:3]),
            ('為什麼重要', ['連結價值、影響與說服力', '把發現轉成可傳達的敘事']),
            ('風險', build_limitations(summary, mode)[:2]),
            ('Next Step / Ask', ['明確提出下一步', '對齊資源、合作或決策需求']),
            ('結語', ['本份 deck 可作為提案或募資簡報的第一版底稿']),
        ]

    parts = [f"# {title}｜簡報稿\n\n- 模式：`{mode}`\n- 等級：`{level}` — {LEVEL_NOTES[level]}\n"]
    slide_no = 1
    for heading, bullets in slides:
        parts.append(f"## Slide {slide_no} — {heading}")
        parts.extend([f"- {x}" for x in bullets])
        parts.append('')
        slide_no += 1

    if level in {'v3', 'v4'} and plots:
        for plot in plots:
            note = interpret_plot(plot, mode)
            parts.append(f"## Slide {slide_no} — {note['title']}")
            parts.append(f"- 圖檔：{plot.name}")
            parts.append(f"- 解讀：{note['summary']}")
            parts.append(f"- 延伸：{note['so_what']}")
            parts.append('')
            slide_no += 1
    return '\n'.join(parts).strip() + '\n'


def make_speaker_notes(title: str, mode: str, summary: dict[str, Any], plots: list[Path], level: str) -> str:
    findings = build_key_findings(summary)
    findings_md = '\n'.join([f"- {x}" for x in findings])
    opener = {
        'academic': '先交代研究背景、研究問題與資料來源，再說明這份內容是研究草稿第一版。',
        'business': '先講這份分析支援哪個決策，再交代這份內容的管理價值與時間敏感性。',
        'pitch': '先抓住聽眾注意力，說明痛點、機會與這份資料為何值得相信。',
    }[mode]
    closer = {
        'academic': '結尾時回到研究限制與後續研究方向。',
        'business': '結尾時回到建議行動與追蹤機制。',
        'pitch': '結尾時回到 ask、資源需求與下一步承諾。',
    }[mode]
    parts = [
        f"# {title}｜Speaker Notes",
        '',
        f"- 模式：`{mode}`",
        f"- 等級：`{level}` — {LEVEL_NOTES[level]}",
        '',
        '## 開場',
        f"- {opener}",
        '',
        '## 重點提示',
        findings_md,
        '',
    ]
    if level in {'v3', 'v4'} and plots:
        parts.extend(['## 逐圖口頭提示', ''])
        for plot in plots:
            note = interpret_plot(plot, mode)
            parts.append(f"### {plot.name}")
            parts.append(f"- {note['summary']}")
            parts.append(f"- {note['so_what']}")
            parts.append('')
    parts.extend(['## 收尾建議', f"- {closer}", '- 針對最重要的一張圖，多講一層其背後的意義與行動建議。', ''])
    return '\n'.join(parts)


def make_deck_html(title: str, audience: str, purpose: str, slides_md: str, plots: list[Path], mode: str, level: str) -> str:
    if level == 'v4':
        theme = {
            'academic': {'primary': '#0f172a', 'accent': '#334155', 'bg': '#eef2ff', 'hero': 'linear-gradient(135deg,#0f172a 0%,#1e293b 55%,#475569 100%)'},
            'business': {'primary': '#0b3b66', 'accent': '#1d4ed8', 'bg': '#eff6ff', 'hero': 'linear-gradient(135deg,#0b3b66 0%,#1d4ed8 60%,#60a5fa 100%)'},
            'pitch': {'primary': '#4c1d95', 'accent': '#7c3aed', 'bg': '#faf5ff', 'hero': 'linear-gradient(135deg,#4c1d95 0%,#7c3aed 60%,#c084fc 100%)'},
        }[mode]
        primary = theme['primary']
        accent = theme['accent']
        bg = theme['bg']
        hero = theme['hero']
        plot_map = {p.name: p for p in plots}
    else:
        primary = '#1f2937'
        accent = '#2563eb'
        bg = '#f6f8fb'
        hero = None
        plot_map = {p.name: p for p in plots}

    slide_blocks = []
    current = []
    current_title = None
    for line in slides_md.splitlines():
        if line.startswith('## Slide '):
            if current_title is not None:
                slide_blocks.append((current_title, current))
            current_title = line.replace('## ', '', 1)
            current = []
        elif line.startswith('- 模式：') or line.startswith('- 等級：') or line.startswith('# '):
            continue
        else:
            current.append(line)
    if current_title is not None:
        slide_blocks.append((current_title, current))

    sections = []
    for heading, body in slide_blocks:
        body_html = []
        referenced_plot = None
        for line in body:
            line = line.strip()
            if not line:
                continue
            if line.startswith('- 圖檔：'):
                plot_name = line.replace('- 圖檔：', '', 1).strip()
                referenced_plot = plot_map.get(plot_name)
                body_html.append(f"<li>{html.escape(line[2:])}</li>")
            elif line.startswith('- '):
                body_html.append(f"<li>{html.escape(line[2:])}</li>")
            else:
                body_html.append(f"<p>{html.escape(line)}</p>")
        img_html = ''
        if referenced_plot and level in {'v3', 'v4'}:
            img_html = f"<div class='plot-single'><img src='{html.escape(referenced_plot.name)}' alt='{html.escape(referenced_plot.name)}' /><div class='plot-caption'>圖：{html.escape(referenced_plot.name)}</div></div>"
        list_items = ''.join(x for x in body_html if x.startswith('<li>'))
        paras = ''.join(x for x in body_html if x.startswith('<p>'))
        list_html = f"<ul>{list_items}</ul>" if list_items else ''
        if level == 'v4':
            sections.append(
                f"<section class='slide'><div class='slide-top'><div class='eyebrow'>{html.escape(mode.upper())}</div>"
                f"<div class='page-tag'>{html.escape(heading.split(' — ')[0])}</div></div><h2>{html.escape(heading)}</h2>{paras}{list_html}{img_html}</section>"
            )
        else:
            sections.append(f"<section class='slide'><h2>{html.escape(heading)}</h2>{paras}{list_html}{img_html}</section>")

    if level == 'v4':
        css = f"""
@page {{ size: A4 landscape; margin: 0; }}
@media print {{
  body {{ background: #fff; padding: 0; }}
  .slide {{ box-shadow: none; margin: 0; min-height: 100vh; border-radius: 0; page-break-after: always; page-break-inside: avoid; border-top-width: 16px; border-top-style: solid; border-top-color: {accent}; }}
  .hero {{ box-shadow: none; margin: 0; min-height: 100vh; border-radius: 0; }}
}}
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans CJK TC', sans-serif; background: {bg}; margin: 0; padding: 32px; color: {primary}; }}
.hero {{ max-width: 1180px; margin: 0 auto 32px; padding: 56px 64px; border-radius: 32px; background: {hero}; color: white; box-shadow: 0 32px 64px rgba(15,23,42,.15); display: flex; flex-direction: column; justify-content: center; min-height: 500px; }}
.hero h1 {{ margin: 12px 0 20px; font-size: 52px; line-height: 1.2; letter-spacing: -0.02em; font-weight: 800; text-wrap: balance; }}
.hero p {{ margin: 8px 0; font-size: 20px; opacity: .9; font-weight: 400; }}
.hero-meta {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-top: 48px; }}
.hero-card {{ background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); border-radius: 20px; padding: 20px 24px; backdrop-filter: blur(10px); }}
.hero-card strong {{ display: block; font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.8; margin-bottom: 6px; }}
.slide {{ background: #fff; border-radius: 32px; padding: 48px 56px; margin: 0 auto 32px; max-width: 1180px; min-height: 660px; box-shadow: 0 16px 48px rgba(15,23,42,.08); page-break-after: always; border-top: 16px solid {accent}; position: relative; overflow: hidden; display: flex; flex-direction: column; }}
.slide::after {{ content: ''; position: absolute; right: -80px; top: -80px; width: 240px; height: 240px; background: radial-gradient(circle, {bg} 0%, rgba(255,255,255,0) 70%); pointer-events: none; }}
.slide-top {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; z-index: 1; }}
h1, h2 {{ margin-top: 0; font-weight: 700; }}
h2 {{ font-size: 36px; margin-bottom: 24px; color: {primary}; letter-spacing: -0.01em; }}
.slide p {{ font-size: 20px; line-height: 1.6; color: #334155; margin-bottom: 16px; }}
.slide ul {{ line-height: 1.6; font-size: 22px; padding-left: 28px; color: #1e293b; margin-top: 8px; flex-grow: 1; }}
.slide li {{ position: relative; padding-left: 8px; }}
.slide li + li {{ margin-top: 14px; }}
.slide li::marker {{ color: {accent}; font-weight: bold; }}
.eyebrow {{ display: inline-flex; align-items: center; padding: 8px 16px; border-radius: 999px; background: {bg}; color: {accent}; font-weight: 800; font-size: 13px; letter-spacing: .1em; box-shadow: 0 2px 8px rgba(0,0,0,0.04); }}
.page-tag {{ color: #94a3b8; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }}
.plot-single {{ margin-top: auto; text-align: center; padding-top: 24px; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; }}
.plot-single img {{ max-width: 100%; max-height: 380px; border: 1px solid #e2e8f0; border-radius: 20px; background: #f8fafc; box-shadow: 0 12px 32px rgba(15,23,42,.06); padding: 8px; }}
.plot-caption {{ margin-top: 14px; font-size: 15px !important; color: #64748b !important; font-style: italic; text-align: center; background: #f1f5f9; padding: 6px 16px; border-radius: 999px; }}
""".strip()
        hero_html = (
            f"<div class='hero'><div class='eyebrow'>{html.escape(mode.upper())}</div>"
            f"<h1>{html.escape(title)}</h1><p>適用對象：{html.escape(audience)}</p><p>目的：{html.escape(purpose)}</p>"
            f"<div class='hero-meta'>"
            f"<div class='hero-card'><strong>等級</strong><br>{html.escape(level)} — {html.escape(LEVEL_NOTES[level])}</div>"
            f"<div class='hero-card'><strong>圖表數量</strong><br>{len(plots)}</div>"
            f"<div class='hero-card'><strong>輸出定位</strong><br>正式 deck / PDF-ready</div>"
            f"</div></div>"
        )
    else:
        css = f"""
body {{ font-family: Arial, 'Noto Sans CJK TC', sans-serif; background: {bg}; margin: 0; padding: 24px; color: {primary}; }}
.hero {{ max-width: 1100px; margin: 0 auto 24px; padding: 8px 6px; }}
.slide {{ background: #fff; border-radius: 18px; padding: 32px; margin: 0 auto 24px; max-width: 1100px; box-shadow: 0 8px 28px rgba(0,0,0,.08); page-break-after: always; border-top: 10px solid {accent}; }}
h1, h2 {{ margin-top: 0; }}
h1 {{ font-size: 40px; }}
ul {{ line-height: 1.7; }}
.plot-single {{ margin-top: 18px; text-align: center; }}
img {{ max-width: 100%; border: 1px solid #ddd; border-radius: 12px; background: #fff; }}
.plot-caption {{ margin-top: 10px; font-size: 14px; color: #6b7280; font-style: italic; }}
""".strip()
        hero_html = (
            f"<div class='hero'><h1>{html.escape(title)}</h1><p>對象：{html.escape(audience)}</p>"
            f"<p>目的：{html.escape(purpose)}</p><p>等級：{html.escape(level)} — {html.escape(LEVEL_NOTES[level])}</p></div>"
        )

    return (
        "<!doctype html><html><head><meta charset='utf-8'>"
        f"<title>{html.escape(title)}</title><style>{css}</style></head><body>"
        + hero_html
        + ''.join(sections)
        + "</body></html>"
    )


def main() -> int:
    parser = argparse.ArgumentParser(
        description='Generate paper/slides bundle from analysis outputs',
        epilog=(
            'Levels: '
            'v2=基礎交付版（paper/slides/speaker-notes/deck）； '
            'v3=洞察強化版（v2 + insights + 每張圖逐頁解讀）； '
            'v4=正式交付版（v3 + 更正式 deck 視覺 + PDF-ready 工作流）'
        ),
    )
    parser.add_argument('--analysis-dir', required=True)
    parser.add_argument('--output-dir', required=True)
    parser.add_argument('--title', default='研究分析草稿')
    parser.add_argument('--audience', default='決策者')
    parser.add_argument('--purpose', default='研究報告')
    parser.add_argument('--mode', default='business', choices=sorted(MODES))
    parser.add_argument(
        '--level',
        default='v4',
        choices=sorted(LEVELS),
        help='輸出等級：v2=基礎交付版；v3=洞察強化版；v4=正式交付版（預設）',
    )
    args = parser.parse_args()

    analysis_dir = Path(args.analysis_dir).expanduser().resolve()
    output_dir = Path(args.output_dir).expanduser().resolve()
    output_dir.mkdir(parents=True, exist_ok=True)

    summary_path = analysis_dir / 'summary.json'
    report_path = analysis_dir / 'report.md'
    if not summary_path.exists():
        raise SystemExit(f'Missing summary.json in {analysis_dir}')
    if not report_path.exists():
        raise SystemExit(f'Missing report.md in {analysis_dir}')

    summary = read_json(summary_path)
    report_md = read_text(report_path)
    plots = find_plots(analysis_dir)
    insights_md = make_insights_md(args.title, args.mode, summary, plots) if args.level in {'v3', 'v4'} else None

    paper_md = make_paper(args.title, args.audience, args.purpose, args.mode, args.level, summary, report_md, plots, insights_md)
    slides_md = make_slides(args.title, args.audience, args.purpose, args.mode, summary, plots, args.level)
    speaker_notes = make_speaker_notes(args.title, args.mode, summary, plots, args.level)
    deck_html = make_deck_html(args.title, args.audience, args.purpose, slides_md, plots, args.mode, args.level)

    for plot in plots:
        dest = output_dir / plot.name
        if dest != plot:
            shutil.copy2(plot, dest)

    (output_dir / 'paper.md').write_text(paper_md, encoding='utf-8')
    (output_dir / 'slides.md').write_text(slides_md, encoding='utf-8')
    (output_dir / 'speaker-notes.md').write_text(speaker_notes, encoding='utf-8')
    (output_dir / 'deck.html').write_text(deck_html, encoding='utf-8')
    if insights_md:
        (output_dir / 'insights.md').write_text(insights_md, encoding='utf-8')

    manifest_outputs = {
        'paper': str(output_dir / 'paper.md'),
        'slides': str(output_dir / 'slides.md'),
        'speakerNotes': str(output_dir / 'speaker-notes.md'),
        'deckHtml': str(output_dir / 'deck.html'),
    }
    if insights_md:
        manifest_outputs['insights'] = str(output_dir / 'insights.md')

    manifest = {
        'title': args.title,
        'audience': args.audience,
        'purpose': args.purpose,
        'mode': args.mode,
        'level': args.level,
        'levelNote': LEVEL_NOTES[args.level],
        'analysisDir': str(analysis_dir),
        'outputs': manifest_outputs,
        'plots': [str(p) for p in plots],
    }
    (output_dir / 'bundle.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8')
    print(json.dumps(manifest, ensure_ascii=False, indent=2))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
