#!/usr/bin/env python3
"""
独立终审/pass 前核验 PDF 批注交付件（2026-06-22 阳澄湖团建实证）。

用于扫描件/PDF 合同走"批注模式"交付后，小Maggie作为总负责人独立亲验——
不只信 workflow final_review 自报。docx 专项检查（编号/INS字体/numPr）对 PDF 不适用，
PDF 批注交付改查以下项：

  1. Nextcloud 交付件 ↔ 本地核验件 SHA256 字节一致（防同步/编码改坏）
  2. 页数：原件 == 交付件
  3. 原文完整性：原件批注数应为 0（确认原文未被改动）
  4. 批注数 + author：高亮(Highlight)与批注气泡(Text)应配对，全部 author=WB
  5. 批注格式合规：内容全以"建议"开头，无【】标签、无理由/原因解释
  6. 高亮几何锚定：落在正文区（非页边/空白），宽度横跨条款行

⚠️ 若交付件大小/页数异常（如读出 0 页、大小骤变），先排查是不是用户在 OnlyOffice
   手动编辑——别急着判定损坏并用本地副本覆盖。见 SKILL.md "已知坑"第一条。

用法:
  python3 verify_pdf_annotation_deliverable.py <原件.pdf> <Nextcloud交付件.pdf> [本地核验件.pdf]
  # 第三个参数可选；给了就做 SHA256 字节一致性比对
"""
import sys, os, hashlib

try:
    import pymupdf
except ImportError:
    import fitz as pymupdf

FORBIDDEN = ['【', '】', '原因', '理由', '因为', '风险']  # 批注禁用 token（保密条款本体"因乙方原因"需人工甄别）

def sha256(p):
    h = hashlib.sha256()
    with open(p, 'rb') as f:
        h.update(f.read())
    return h.hexdigest()

def main():
    if len(sys.argv) < 3:
        print(__doc__); sys.exit(1)
    orig, deliv = sys.argv[1], sys.argv[2]
    local = sys.argv[3] if len(sys.argv) > 3 else None
    ok = True

    # 1. SHA256 字节一致
    if local:
        s_d, s_l = sha256(deliv), sha256(local)
        same = s_d == s_l
        print(f"[1] SHA256 NC↔本地 {'✅一致' if same else '❌不一致'}  NC={s_d[:16]} 本地={s_l[:16]}")
        ok &= same

    do, dd = pymupdf.open(orig), pymupdf.open(deliv)

    # 2. 页数
    p_ok = len(do) == len(dd)
    print(f"[2] 页数 原件{len(do)}==交付{len(dd)} {'✅' if p_ok else '❌'}")
    ok &= p_ok
    if len(dd) == 0:
        print("   ⚠️ 交付件读出 0 页！先查是不是用户在 OnlyOffice 改动/后台编码，别判定损坏就覆盖。")
        ok = False

    # 3. 原文完整性
    orig_annots = sum(len(list(p.annots() or [])) for p in do)
    print(f"[3] 原件批注数={orig_annots} {'✅原文未改动' if orig_annots == 0 else '⚠️原件本身带批注'}")

    # 4 & 5. 批注数/author/格式
    total = 0; authors = set(); types = {}; bad_fmt = []
    for pi, page in enumerate(dd):
        for a in page.annots() or []:
            total += 1
            info = a.info
            authors.add(info.get('title', ''))
            t = a.type[1]; types[t] = types.get(t, 0) + 1
            c = (info.get('content', '') or '').strip()
            if c:
                if not c.startswith('建议'):
                    bad_fmt.append(f"P{pi+1}: 非'建议'开头: {c[:30]}")
                for tok in FORBIDDEN:
                    if tok in c:
                        bad_fmt.append(f"P{pi+1}: 含禁用token[{tok}]: {c[:30]}")
    a_ok = authors <= {'WB'} and total > 0
    print(f"[4] 批注总数={total} 类型={types} author={authors} {'✅' if a_ok else '❌'}")
    ok &= a_ok
    if bad_fmt:
        print(f"[5] ❌批注格式问题{len(bad_fmt)}处:")
        for b in bad_fmt[:10]:
            print(f"    {b}")
        ok = False
    else:
        print(f"[5] 批注格式 ✅全以'建议'开头、无【】/理由")

    # 6. 高亮几何锚定（落正文非页边）
    print(f"[6] 高亮锚定位置:")
    for pi, page in enumerate(dd):
        ph = page.rect.height
        for a in page.annots() or []:
            if a.type[1] == 'Highlight':
                r = a.rect
                yp = r.y0 / ph * 100
                flag = '✅' if (5 < yp < 95 and r.width > 200) else '⚠️疑似页边/过窄'
                print(f"    P{pi+1} y={yp:.0f}%高 宽{r.width:.0f}px {flag}")

    print(f"\n{'='*40}\n总核验: {'✅ 全部通过，可交付/登记' if ok else '❌ 有问题，先排查再说'}")
    sys.exit(0 if ok else 2)

if __name__ == '__main__':
    main()
