#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""把翻页式 deck HTML 转写成 Markdown（供后续在 MD 层面改数据）。
- 逐页输出：标题 + 正文文本 + Markdown 表格 + 图表数据表
- 末尾附「保真自检」：原稿每个表格单元格/文本框文字都必须出现在 MD 里

用法：/usr/bin/python3 脚本/deck_html_to_md.py <in.html> <out.md> [deck_model.json]
"""
import io, re, sys, json, html as _h

src, dst = sys.argv[1], sys.argv[2]
model = sys.argv[3] if len(sys.argv) > 3 else None

s = io.open(src, encoding='utf-8').read()

# ---------- 0. 侧边目录（页面标题） ----------
toc = {}
for m in re.finditer(r'<a href="#p(\d+)"[^>]*>(.*?)</a>', s, re.S):
    toc[int(m.group(1))] = clean(m.group(2)) if False else None  # placeholder


def clean(x):
    x = re.sub(r'<(script|style)[^>]*>.*?</\1>', '', x, flags=re.S)
    x = re.sub(r'<br\s*/?>', '\n', x)
    x = re.sub(r'</p>', '\n', x)
    x = re.sub(r'<[^>]+>', '', x)
    x = _h.unescape(x)
    x = x.replace('\u2003', ' ').replace('\xa0', ' ')
    x = re.sub(r'[ \t]+', ' ', x)
    x = re.sub(r'\n\s*\n+', '\n', x)
    return x.strip()


TOC = {}
for m in re.finditer(r'<a href="#p(\d+)"[^>]*>(.*?)</a>', s, re.S):
    TOC[int(m.group(1))] = clean(m.group(2))

# ---------- 1. 切页 ----------
marks = list(re.finditer(r'<div class="slide[^"]*"[^>]*id="s(\d+)"', s))
if not marks:
    marks = list(re.finditer(r'<div class="slide[^"]*"[^>]*>', s))
slides = []
for i, m in enumerate(marks):
    a = m.end()
    b = marks[i + 1].start() if i + 1 < len(marks) else len(s)
    n = int(m.group(1)) if m.lastindex else i + 1
    slides.append((n, s[a:b]))

# ---------- 2. 逐页转写 ----------
out = []
out.append('# 生命科学销售部 2026 年三季度工作总结报告')
out.append('')
out.append('> 转写自 `销售分析/生命科学销售部_2026三季度工作汇报_HTML版.html`（翻页式 deck，14 页）')
out.append('> 转写日期：2026-09-30 ｜ 原稿 PPTX 的文字与数字**一个未改**')
out.append('')
out.append('---')
out.append('')

for n, chunk in slides:
    title = TOC.get(n) or ('第 %d 页' % n)
    out.append('## P%d · %s' % (n, title))
    out.append('')

    # 图表（放前面，标注是什么图）
    for m in re.finditer(r'<div class="ch" data-kind="(\w+)"[^>]*data-names="([^"]*)"[^>]*>', chunk):
        kind = m.group(1)
        names = [x for x in _h.unescape(m.group(2)).split('|') if x]
        out.append('> 📊 **图表（%s）**：数据取自本页表格，改表格数字图会自动重绘%s'
                   % ('饼图' if kind == 'pie' else '柱状图',
                      ('；系列：' + ' / '.join(names)) if names else ''))
        out.append('')

    # 表格
    for m in re.finditer(r'<table[^>]*>(.*?)</table>', chunk, re.S):
        rows = []
        for tr in re.findall(r'<tr[^>]*>(.*?)</tr>', m.group(1), re.S):
            cells = []
            for td in re.findall(r'<t[dh][^>]*>(.*?)</t[dh]>', tr, re.S):
                txt = clean(td).replace('\n', '<br>')
                cells.append(txt)
            if any(c.strip() for c in cells):
                rows.append(cells)
        # 去掉 colspan/rowspan 造成的空尾列
        w = max(len(r) for r in rows) if rows else 0
        for r in rows:
            while len(r) < w:
                r.append('')
        if not rows:
            continue
        out.append('| ' + ' | '.join(rows[0]) + ' |')
        out.append('|' + '---|' * len(rows[0]))
        for r in rows[1:]:
            out.append('| ' + ' | '.join(r) + ' |')
        out.append('')

    # 正文文本（排除表内文字）
    plain = re.sub(r'<table[^>]*>.*?</table>', '', chunk, flags=re.S)
    for m in re.finditer(r'<div class="t[xx]"[^>]*>(.*?)</div>', plain, re.S):
        t = clean(m.group(1))
        if not t:
            continue
        for line in t.split('\n'):
            line = line.strip()
            if line:
                out.append('- ' + line)
    if re.search(r'<div class="ole"', chunk):
        out.append('> ⚠️ 原稿此处为内嵌对象（OLE），HTML 版以占位框呈现，不伪造内容。')
    out.append('')
    out.append('---')
    out.append('')

md = '\n'.join(out).rstrip() + '\n'
# 收尾：去掉连续分隔线
md = re.sub(r'(---\n\n)+(?=---\n\n)', '', md)
io.open(dst, 'w', encoding='utf-8').write(md)

print('✅ 已写出 %s' % dst)
print('   %d 页 / %d 行 / %.1f KB' % (len(slides), md.count('\n') + 1, len(md.encode()) / 1024.0))

# ---------- 3. 保真自检 ----------
if model:
    M = json.load(io.open(model, encoding='utf-8'))
    flat = re.sub(r'<br\s*/?>', '\n', md)
    flat = re.sub(r'[ \t]+', '', flat)
    units, miss = 0, []
    for sl in M['slides']:
        for layer in ('shapes', 'layshapes'):
            for sh in sl.get(layer, []):
                if sh.get('kind') == 'table':
                    for row in sh.get('grid') or []:
                        for c in row:
                            if not isinstance(c, dict):
                                continue
                            if c.get('spanned'):
                                continue
                            t = c.get('t') or ''
                            for part in t.split('\n'):
                                part = re.sub(r'\s+', '', part)
                                if len(part) < 2:
                                    continue
                                units += 1
                                if part not in flat:
                                    miss.append('P%d 表格「%s」' % (sl['n'], part[:26]))
                elif sh.get('kind') == 'text':
                    for part in (sh.get('text') or '').split('\n'):
                        part = re.sub(r'\s+', '', part)
                        if len(part) < 2:
                            continue
                        units += 1
                        if part not in flat:
                            miss.append('P%d 文本「%s」' % (sl['n'], part[:26]))
    print('   保真自检：校验单元 %d 个 / 未命中 %d 个' % (units, len(miss)))
    for x in miss[:20]:
        print('      ✗ %s' % x)
    print('   %s' % ('✅ MD 已覆盖原稿全部文字与数字' if not miss else '❌ 有遗漏'))
