#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""SEC-HPLC 测 MIX 中酶纯度与浓度 · 调研报告生成器
MD → 自包含 HTML（宝锐蓝白版式 #1F4E79 + 白底卡片 + brand_kit 顶部 Logo 栏）
零外链、内联 CSS/SVG/Logo。用法：
  /usr/bin/python3 脚本/build_sec_hplc_report.py <in.md> <out.html>
"""
import html, os, re, sys
import markdown

BASE = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
sys.path.insert(0, os.path.join(BASE, '脚本'))
import brand_kit as BK

IN = sys.argv[1] if len(sys.argv) > 1 else os.path.join(BASE, '知识沉淀/SEC-HPLC调研_正文_20261010.md')
OUT = sys.argv[2] if len(sys.argv) > 2 else os.path.join(BASE, '知识沉淀/SEC-HPLC_测MIX中酶纯度与浓度_调研报告_20261010.html')

md = open(IN, encoding='utf-8').read()

# ── 图 1：SEC 分离原理（柱内分子筛 + UV280 色谱峰）──────────────────
def fig_sec():
    return '''<svg viewBox="0 0 1080 400" class="fig" role="img" aria-label="SEC体积排阻分离原理与色谱峰示意">
  <style>
    .ft{font-size:15px;font-weight:700;fill:#1F4E79}
    .fs{font-size:12.5px;fill:#5b6470}
    .fk{font-size:11.5px;fill:#848b96}
    .fz{font-size:12px;font-weight:700}
  </style>
  <text x="28" y="30" class="ft">图 1 · SEC（体积排阻）分离原理：按「分子流体力学体积」大小排先后</text>
  <!-- 左：柱内分子筛机理 -->
  <rect x="28" y="52" width="360" height="300" rx="12" fill="#f7f9fc" stroke="#e3e8ef"/>
  <rect x="176" y="70" width="64" height="250" rx="10" fill="#eaf1fb" stroke="#c9d9f0"/>
  <circle cx="208" cy="110" r="26" fill="#2E75B6" opacity=".9"/>
  <text x="208" y="114" class="fz" text-anchor="middle" fill="#fff">大</text>
  <circle cx="208" cy="250" r="13" fill="#5b8fd4"/>
  <text x="208" y="254" class="fz" text-anchor="middle" fill="#fff" style="font-size:10px">小</text>
  <circle cx="208" cy="200" r="4" fill="#9fb4d0"/><circle cx="196" cy="176" r="4" fill="#9fb4d0"/>
  <circle cx="222" cy="222" r="4" fill="#9fb4d0"/><circle cx="198" cy="292" r="4" fill="#9fb4d0"/>
  <circle cx="220" cy="150" r="4" fill="#9fb4d0"/>
  <text x="60" y="118" class="fs">大分子（聚集体）</text>
  <text x="60" y="136" class="fk">不能进入孔隙 → 走外部空隙</text>
  <text x="60" y="154" class="fk">→ 最先流出（保留时间短）</text>
  <text x="60" y="300" class="fs">小分子（片段/杂质）</text>
  <text x="60" y="318" class="fk">进入孔隙、路径长 → 最后流出</text>
  <text x="208" y="342" class="fk" text-anchor="middle">填充微球（分子筛）</text>
  <!-- 右：色谱峰 -->
  <text x="430" y="72" class="fs" style="font-weight:700;fill:#1F4E79">UV 280 nm 色谱图（峰面积归一化 = 纯度口径）</text>
  <line x1="450" y1="300" x2="1040" y2="300" stroke="#b9c4d6" stroke-width="2"/>
  <line x1="450" y1="90" x2="450" y2="300" stroke="#b9c4d6" stroke-width="2"/>
  <text x="452" y="86" class="fk">A280</text>
  <text x="1000" y="322" class="fk">洗脱时间 / 体积 →</text>
  <!-- 聚集体峰（早） -->
  <path d="M470,300 C500,300 508,150 540,150 C572,150 580,300 610,300" fill="#d9534f" opacity=".28" stroke="#d9534f" stroke-width="2"/>
  <text x="540" y="140" class="fz" text-anchor="middle" fill="#b53f3b">聚集体/多聚体</text>
  <text x="540" y="322" class="fk" text-anchor="middle">早出峰</text>
  <!-- 单体主峰 -->
  <path d="M620,300 C650,300 660,95 700,95 C740,95 750,300 780,300" fill="#1F4E79" opacity=".26" stroke="#1F4E79" stroke-width="2.4"/>
  <text x="700" y="85" class="fz" text-anchor="middle" fill="#1F4E79">单体主峰（目标酶）</text>
  <text x="700" y="322" class="fk" text-anchor="middle">纯度 = 主峰面积 ÷ 总面积</text>
  <!-- 片段峰（晚） -->
  <path d="M800,300 C820,300 828,225 855,225 C882,225 890,300 912,300" fill="#0d904f" opacity=".25" stroke="#0d904f" stroke-width="2"/>
  <text x="856" y="215" class="fz" text-anchor="middle" fill="#0d904f">片段/低分子量杂质</text>
  <text x="856" y="322" class="fk" text-anchor="middle">晚出峰</text>
</svg>'''

# ── 图 2：方法对比矩阵 ──────────────────────────────────────────────
def fig_matrix():
    attrs = ['尺寸异质性\n(聚集/片段)', '绝对浓度\n(g/L)', '功能性\n比活性', '身份\n(序列/质量)', '单酶\n放行适用']
    methods = [
        ('SEC-HPLC',            [3, 1, 0, 1, 3]),
        ('AEX(离子交换)',        [2, 1, 0, 1, 2]),
        ('CE-SDS',              [3, 0, 0, 1, 3]),
        ('SDS-PAGE',            [3, 0, 0, 1, 2]),
        ('酶活测定(功能法)',      [0, 2, 3, 0, 3]),
        ('A280/消光系数',        [0, 3, 0, 0, 3]),
        ('质谱(LC-MS)',          [1, 2, 0, 3, 1]),
    ]
    col_note = ['峰面积归一', '需标准/校准', '活性单位法', '肽段/质量指纹', '批次放行']
    X0, Y0, CW, RH = 260, 108, 150, 40
    def cell(v):
        return {3: ('#1F4E79', '#fff', '●'), 2: ('#6f9dd0', '#fff', '◐'),
                1: ('#cfe0f4', '#1F4E79', '◔'), 0: ('#f1f3f6', '#a6adb8', '○')}[v]
    rows = []
    for i, (name, vals) in enumerate(methods):
        y = Y0 + i * RH
        bg = '#f7f9fc' if i % 2 == 0 else '#fff'
        rows.append(f'<rect x="28" y="{y}" width="{X0-28+CW*len(attrs)}" height="{RH}" fill="{bg}"/>')
        rows.append(f'<text x="40" y="{y+25}" class="fz" fill="#1f2328">{name}</text>')
        for j, v in enumerate(vals):
            fx, tx, gl = cell(v)
            cx = X0 + j * CW
            rows.append(f'<rect x="{cx+16}" y="{y+6}" width="{CW-60}" height="28" rx="7" fill="{fx}"/>')
            rows.append(f'<text x="{cx+54}" y="{y+26}" text-anchor="middle" fill="{tx}" style="font-size:14px">{gl}</text>')
    cols = []
    for j, a in enumerate(attrs):
        cx = X0 + j * CW
        l1, l2 = a.split('\n')
        cols.append(f'<text x="{cx+54}" y="66" class="fk" text-anchor="middle">{l1}</text>')
        cols.append(f'<text x="{cx+54}" y="82" class="fk" text-anchor="middle">{l2}</text>')
        cols.append(f'<text x="{cx+54}" y="98" class="fk" text-anchor="middle" style="fill:#b9c4d6">{col_note[j]}</text>')
    legend = [(3, '强 / 直接回答'), (2, '中'), (1, '弱 / 间接'), (0, '不能回答')]
    lx = 300
    legs = []
    for v, lab in legend:
        fx, tx, gl = cell(v)
        legs.append(f'<rect x="{lx}" y="386" width="16" height="16" rx="4" fill="{fx}"/>')
        legs.append(f'<text x="{lx+22}" y="399" class="fk">{lab}</text>')
        lx += 170
    return f'''<svg viewBox="0 0 1080 410" class="fig" role="img" aria-label="单酶质控方法对比矩阵">
  <style>.fz{{font-size:13px;font-weight:700}}.fk{{font-size:11.5px;fill:#5b6470}}</style>
  <text x="28" y="30" style="font-size:15px;font-weight:700;fill:#1F4E79">图 2 · 单酶质控方法 × 关键质量属性 能力矩阵（同一方法不同属性上强弱不同，须组合使用）</text>
  {''.join(cols)}{''.join(rows)}{''.join(legs)}
</svg>'''

# ── 图 3：逐酶工作流决策图 ──────────────────────────────────────────
def fig_flow():
    enzymes = [('Taq 聚合酶', '#1F4E79', '~94 kDa'),
               ('UNG 尿嘧啶糖苷酶', '#0d904f', '~25–26 kDa'),
               ('逆转录酶 MMLV', '#b45309', '~75–78 kDa')]
    boxes = []
    steps = ['来料/原液', 'SEC-HPLC\n纯度(峰面积归一)', 'A280+ε\n或 酶活法 浓度', '批间趋势\nLot-to-Lot', '放行判定']
    y0 = 74
    for k, (nm, col, mw) in enumerate(enzymes):
        y = y0 + k * 104
        boxes.append(f'<rect x="28" y="{y}" width="150" height="72" rx="10" fill="{col}" opacity=".9"/>')
        boxes.append(f'<text x="103" y="{y+30}" class="fz" text-anchor="middle" fill="#fff" style="font-size:13px">{nm}</text>')
        boxes.append(f'<text x="103" y="{y+52}" class="fk" text-anchor="middle" fill="#eaf1fb">分子量 {mw}</text>')
        for i, s in enumerate(steps):
            x = 214 + i * 168
            bits = s.split('\n')
            l1 = bits[0]
            l2 = bits[1] if len(bits) > 1 else ''
            fc = '#eaf1fb' if i < 4 else '#1F4E79'
            tc = '#1F4E79' if i < 4 else '#fff'
            boxes.append(f'<rect x="{x}" y="{y+8}" width="148" height="56" rx="9" fill="{fc}" stroke="{col}" stroke-width="1.2"/>')
            boxes.append(f'<text x="{x+74}" y="{y+34}" class="fz" text-anchor="middle" fill="{tc}" style="font-size:12px">{l1}</text>')
            boxes.append(f'<text x="{x+74}" y="{y+50}" class="fk" text-anchor="middle" fill="{tc if i==4 else "#5b6470"}">{l2}</text>')
            if i < len(steps) - 1:
                ax = x + 148
                boxes.append(f'<path d="M{ax} {y+36} l10 0" stroke="{col}" stroke-width="1.6" marker-end="url(#ar)"/>')
    return f'''<svg viewBox="0 0 1080 400" class="fig" role="img" aria-label="逐酶质控工作流决策图">
  <defs>
    <marker id="ar" markerWidth="7" markerHeight="7" refX="5" refY="3" orient="auto">
      <path d="M0 0 L6 3 L0 6 z" fill="#5b6470"/>
    </marker>
  </defs>
  <style>.fz{{font-size:13px;font-weight:700}}.fk{{font-size:11.5px;fill:#5b6470}}</style>
  <text x="28" y="36" style="font-size:15px;font-weight:700;fill:#1F4E79">图 3 · 逐酶（Taq / UNG / 逆转录酶）质控工作流：三酶条件各自独立，浓度定位不可只靠 SEC</text>
  <text x="28" y="58" class="fk">每个酶单独走一条链路（同一套 SEC 柱/流动相不通用）；SEC 出「纯度+尺寸异质性」，浓度由 A280 或酶活法给。</text>
  {''.join(boxes)}
  <rect x="214" y="356" width="148" height="0" fill="none"/>
</svg>'''


# ── 渲染 ──────────────────────────────────────────────────────────
parts = re.split(r'^## ', md, flags=re.M)
head_blob = parts[0]
sections = []
for p in parts[1:]:
    lines = p.split('\n')
    title = lines[0].strip()
    body_md = '\n'.join(lines[1:])          # 标题单独渲染，正文去掉首行，避免 <h2> 重复
    sections.append((title, body_md))


def inline(s):
    s = html.escape(str(s), quote=False)
    for a, b in [('✓全文', '<span class="pill ok">✓全文</span>'),
                 ('⚠摘要', '<span class="pill warn">⚠摘要</span>'),
                 ('未核实', '<span class="pill gray">未核实</span>'),
                 ('P0', '<span class="pill pri">P0</span>'),
                 ('P1', '<span class="pill pri1">P1</span>'),
                 ('P2', '<span class="pill pri2">P2</span>'),
                 ('P3', '<span class="pill pri3">P3</span>')]:
        s = s.replace(a, b)
    s = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', s)
    s = re.sub(r'`([^`]+?)`', r'<code>\1</code>', s)
    return s


FIGMAP = {'[[FIG_SEC]]': fig_sec(), '[[FIG_MATRIX]]': fig_matrix(), '[[FIG_FLOW]]': fig_flow()}


def render(mdtext):
    h = markdown.markdown(mdtext, extensions=['tables', 'fenced_code', 'sane_lists'])
    def fix_row(m):
        return re.sub(r'<(td|th)>(.*?)</\1>',
                      lambda x: '<%s>%s</%s>' % (x.group(1), inline(re.sub(r'<[^>]+>', '', x.group(2))), x.group(1)),
                      m.group(0), flags=re.S)
    h = re.sub(r'<tr>.*?</tr>', fix_row, h, flags=re.S)
    for ph, svg in FIGMAP.items():
        h = h.replace('<p>%s</p>' % ph, svg).replace(ph, svg)
    return h

# KPI 卡
kpis = []
m0 = next((t for t in sections if t[0].startswith('0.')), None)
if m0:
    for row in re.findall(r'^\| (\d) \| \*\*(.+?)\*\*：(.+?) \| (.+?) \|$', m0[1], re.M)[:4]:
        kpis.append(row)

toc = ''.join('<li><a href="#s%d">%s</a></li>' % (i, html.escape(t)) for i, (t, _) in enumerate(sections))
lead = render(head_blob) if head_blob.strip() else ''
lead_html = '<section class="lead">%s</section>' % lead if lead else ''
secs_html = ''.join('<section id="s%d"><h2>%s</h2>%s</section>' % (i, html.escape(t), render(blk))
                      for i, (t, blk) in enumerate(sections))

kpi_html = ''
if kpis:
    kpi_html = '<div class="kpi-row">' + ''.join(
        '<div class="kpi"><div class="kpi-n">%s</div><div class="kpi-t">%s</div><div class="kpi-d">%s</div></div>'
        % (n, html.escape(t[:26]), d[:120]) for n, t, d, _ in kpis) + '</div>'

CSS = """
:root{--pri:#1F4E79;--pri2:#2E75B6;--bg:#f5f7fa;--card:#fff;--txt:#1f2328;--sub:#5b6470;--line:#e3e8ef;--ok:#0d904f;--warn:#e37400;--gray:#848b96;}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--txt);font:15px/1.75 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif;display:flex;flex-direction:column;min-height:100vh}
.wrap{max-width:1180px;margin:26px auto;padding:0 20px;display:grid;grid-template-columns:238px 1fr;gap:26px;flex:1 0 auto;width:100%}
@media(max-width:980px){.wrap{grid-template-columns:1fr}.toc{display:none}}
.toc{position:sticky;top:20px;align-self:start;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px}
.toc h3{margin:2px 0 10px;font-size:13px;color:var(--sub);letter-spacing:.6px}
.toc ul{list-style:none;margin:0;padding:0;font-size:13px}
.toc li{margin:6px 0} .toc a{color:var(--pri);text-decoration:none} .toc a:hover{text-decoration:underline}
main{min-width:0}
section{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:22px 26px;margin-bottom:20px;box-shadow:0 1px 3px rgba(16,24,40,.04)}
h1{font-size:25px;line-height:1.4;margin:6px 0 8px;color:var(--pri)}
h2{font-size:18px;color:var(--pri);margin:2px 0 14px;padding-bottom:9px;border-bottom:2px solid var(--pri2)}
h3{font-size:15.5px;margin:18px 0 8px;color:var(--pri)}
blockquote{margin:0 0 14px;padding:10px 14px;background:#eef4fb;border-left:4px solid var(--pri2);border-radius:0 8px 8px 0;color:#33414f;font-size:13.5px}
table{width:100%;border-collapse:collapse;margin:12px 0 18px;font-size:13px;display:block;overflow-x:auto}
th,td{border:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:top}
th{background:#eef4fb;color:var(--pri);font-weight:600}
tr:nth-child(even) td{background:#fafbfd}
code{background:#f0f3f7;padding:1px 5px;border-radius:4px;font-size:12px;color:#1f4e79}
.pill{display:inline-block;padding:1px 7px;border-radius:9px;font-size:11.5px;font-weight:600;white-space:nowrap}
.pill.ok{background:#e6f6ec;color:var(--ok)} .pill.warn{background:#fff3e0;color:var(--warn)}
.pill.gray{background:#eef0f3;color:var(--gray)} .pill.pri{background:#e8f0fa;color:var(--pri);font-weight:700}
.pill.pri1{background:#eaf3fb;color:#2E75B6} .pill.pri2{background:#f1f0fb;color:#5b53c9} .pill.pri3{background:#f3f4f6;color:#6b7280}
svg.fig{width:100%;height:auto;display:block;background:#fff;border:1px solid var(--line);border-radius:12px;margin:14px 0;padding:6px}
.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin:6px 0 18px}
.kpi{background:var(--card);border:1px solid var(--line);border-left:4px solid var(--pri2);border-radius:10px;padding:14px}
.kpi-n{font-size:21px;font-weight:800;color:var(--pri);line-height:1}
.kpi-t{font-size:13.5px;font-weight:700;margin:6px 0 4px}
.kpi-d{font-size:12.5px;color:var(--sub)}
section.lead{background:linear-gradient(180deg,#fbfdff,#fff)}
section.lead h1{margin-top:0}
ul,ol{margin:8px 0 8px 22px;padding:0} li{margin:5px 0}
"""

H = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>SEC-HPLC 测 MIX 中酶（Taq/UNG/逆转录酶）纯度与浓度 · 调研报告</title>
<style>%s</style></head><body>
<div class="wrap"><nav class="toc"><h3>目录</h3><ul>%s</ul></nav><main>%s</main></div>
</body></html>"""

out = H % (CSS, toc, lead_html + kpi_html + secs_html)
out = BK.inject(out, theme='dark', tag='营销中心 · 技术情报',
                footer_extra='SEC-HPLC 单酶质控调研 · 生成于 2026-10-10 ｜ 内部资料，含客户信息，不得对外发送')

os.makedirs(os.path.dirname(OUT), exist_ok=True)
open(OUT, 'w', encoding='utf-8').write(out)

# ── 自检 ──
ext = re.findall(r'(?:src|href)\s*=\s*["\'](?:https?:)?//[^"\']+', out)
svgs = re.findall(r'<svg[^>]*class="fig".*?</svg>', out, re.S)
nt = sum(s.count('<title') for s in svgs)
nbr = sum(s.count('<br') for s in svgs)
nrect = sum(s.count('<rect') for s in svgs)
print('✓ 已生成 %s' % OUT)
print('  %.1f KB ｜ 节数 %d ｜ KPI 卡 %d ｜ 表格 %d ｜ svg.fig %d' %
      (os.path.getsize(OUT) / 1024, len(sections), len(kpis), out.count('<table'), len(svgs)))
print('  零外链：外链资源 %d 处 %s' % (len(ext), ext[:3] if ext else '✅'))
print('  品牌：Logo %s ｜ slogan %s ｜ header %d ｜ footer %d' %
      ('✅' if 'data:image/png;base64' in out else '❌', '✅' if '守护生命健康，缔造美好生活' in out else '❌',
       out.count('class="bior-header'), out.count('class="bior-footer')))
print('  SVG 守卫：<title> %d ｜ <br> %d ｜ rect %d  %s' %
      (nt, nbr, nrect, '✅' if (nt == 0 and nbr == 0 and nrect > 5 and len(svgs) >= 3) else '❌ 会破坏渲染'))
assert nt == 0 and nbr == 0 and nrect > 5 and len(svgs) >= 3, 'SVG 内容不达标'
assert len(ext) == 0, '存在外链'
assert BK.verify(out) and all(BK.verify(out).values()), BK.verify(out)
