#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""MD 结果页生成：动画 + RMSD/RMSF + 方法与边界
用法：python build_md_report.py <md_dir> <蛋白名> <uniprot> <pdb_id> <作用> <域说明>
"""
import base64
import io
import json
import os
import sys

sys.path.insert(0, '/Users/liuxinyuan/Desktop/Hermes输出-工作类/脚本')
import brand_kit as BK  # noqa: E402

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
d, name, uniprot, pdbid, role, domdesc = sys.argv[1:7]
s = json.load(open(os.path.join(d, 'summary.json'), encoding='utf-8'))


def b64(p, mime):
    return 'data:%s;base64,%s' % (mime, base64.b64encode(open(p, 'rb').read()).decode())


gif = b64(os.path.join(d, 'md.gif'), 'image/gif')
vid = b64(os.path.join(d, 'md.mp4'), 'video/mp4')
curve = b64(os.path.join(d, 'md_curves.png'), 'image/png')
mp4 = os.path.join(d, 'md.mp4')

import csv as _csv
_rr = list(_csv.DictReader(open(os.path.join(d, 'rmsd.csv'))))
_vals = [float(x['rmsd_A']) for x in _rr]
import statistics as _st
_a = _vals[int(len(_vals) * 0.8):int(len(_vals) * 0.9)]
_b = _vals[int(len(_vals) * 0.9):]
_plateau = abs(_st.mean(_b) - _st.mean(_a)) < 0.3
_gap = abs(_st.mean(_b) - _st.mean(_a))
_drift = _vals[-1] - _vals[int(len(_vals) * 0.2)]
_drg = s['rg_end_A'] - s['rg_start_A']
_interp = []
if _drg < -0.4:
    _interp.append('回转半径由 <b>%.1f Å</b> 降到 <b>%.1f Å</b>（%.1f%%），结构在模拟中<b>明显压实</b>。这是隐式溶剂（GB）模型的已知偏差——缺少显式水与离子，疏水塌缩被放大；<b>不能</b>据此判断酶在真实缓冲体系中会收缩。'
                    % (s['rg_start_A'], s['rg_end_A'], abs(_drg) / s['rg_start_A'] * 100))
elif _drg > 0.4:
    _interp.append('回转半径由 %.1f Å 增至 %.1f Å，结构略舒展。' % (s['rg_start_A'], s['rg_end_A']))
else:
    _interp.append('回转半径基本稳定（%.1f → %.1f Å），整体紧凑度未见明显变化。' % (s['rg_start_A'], s['rg_end_A']))
if _plateau:
    _interp.append('最后 1 ns 的 RMSD 均值 <b>%.2f Å</b>，与前 0.5 ns 仅差 <b>%.2f Å</b>，已进入平台段；但 5 ns 仍不足以证明充分平衡，只能说明<b>这一段</b>没有持续单向漂移。' % (_st.mean(_b), _gap))
else:
    _interp.append('最后 1 ns 的 RMSD 均值较前一区间仍变化 <b>%.2f Å</b>（末帧 %.2f Å），<b>未达平台</b>；此时任何“稳定性”结论都不成立，只能用于观察局部柔性。' % (_gap, _vals[-1]))
_interp.append('柔性最大的区域集中在 <b>N/C 两端</b>（%s）；末端涨落在多数酶里属正常，不应解读为活性位点不稳。'
                % '、'.join('%s%d' % (x['name'], x['resSeq']) for x in s['top_flex_residues'][:4]))
_interp.append('本体系<b>未包含 DNA 底物、Mg²⁺ 与结晶水</b>：UNG 的催化构象需要底物结合才被组织起来，无底物模拟只能看 apo 态的呼吸运动。')
_interp_html = ''.join('<li>%s</li>' % x for x in _interp)

flex = ''.join('<tr><td>%s%d</td><td class="n">%.2f</td></tr>' % (x['name'], x['resSeq'], x['rmsf'])
               for x in s['top_flex_residues'])

page = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"><title>%(name)s 分子动力学模拟</title>
<style>
*{box-sizing:border-box}body{margin:0;background:#f4f6fa;font-family:-apple-system,"PingFang SC",sans-serif;color:#1b2430;font-size:13px;line-height:1.65}
.wrap{max-width:1120px;margin:0 auto;padding:0 18px 40px}
h1{font-size:21px;margin:14px 0 4px}.meta{color:#5f6b7a;font-size:12.2px;margin-bottom:12px}
.card{background:#fff;border:1px solid #e3e8f1;border-radius:10px;padding:14px 16px;margin-bottom:14px}
.card h2{font-size:15px;margin:0 0 10px;padding-left:9px;border-left:3px solid #1F4E79}
.kpi{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
.kpi .k{background:#f7f9fd;border:1px solid #dfe6f2;border-radius:8px;padding:10px 12px}
.kpi .k b{display:block;font-size:20px;color:#1852B6}
.kpi .k span{font-size:11.4px;color:#6b7684}
.two{display:grid;grid-template-columns:1.1fr 1fr;gap:14px}
img{max-width:100%%;border-radius:8px;border:1px solid #e3e8f1}
table{width:100%%;border-collapse:collapse;font-size:12px}
th{background:#1F4E79;color:#fff;padding:6px;text-align:left}td{padding:5px 6px;border-bottom:1px solid #eef1f6}
td.n{text-align:right;font-variant-numeric:tabular-nums}
.warn{background:#FFF6E5;border:1px solid #F3D9A6;border-radius:8px;padding:10px 12px;font-size:12.3px}
ol,ul{margin:6px 0 0 18px;padding:0}li{margin:3px 0}
@media(max-width:900px){.two,.kpi{grid-template-columns:1fr}.bior-tagwrap{display:none!important}}
</style></head><body><div class="wrap">
<h1>%(name)s 分子动力学模拟（隐式溶剂）</h1>
<div class="meta">UniProt %(uniprot)s ｜ 起始结构 PDB %(pdbid)s ｜ OpenMM 8.6.1 · AMBER14 + GBSA-OBC2 · 300 K ｜ 截至 2026-10-17</div>

<div class="card"><div class="kpi">
<div class="k"><b>%(ns).2f ns</b><span>模拟时长</span></div>
<div class="k"><b>%(atoms)s</b><span>主链原子数（含氢）</span></div>
<div class="k"><b>%(rmsd).2f Å</b><span>主链 RMSD 均值</span></div>
<div class="k"><b>%(rmsf).2f Å</b><span>CA RMSF 均值</span></div>
</div></div>

<div class="card"><h2>轨迹动画 <span>每帧 %(frame_ps).1f ps，共 %(frames)d 帧</span></h2>
<div class="two">
<div><video src="%(vid)s" autoplay loop muted playsinline controls style="width:100%%;border-radius:8px;border:1px solid #e3e8f1"></video>
<div style="font-size:11.4px;color:#6b7684;margin-top:4px">若视频不自动播放，点一下播放键；源文件 <code>数据/md/%(dirname)s/md.mp4</code></div></div>
<div><img src="%(curve)s" alt="RMSD / RMSF 曲线"></div>
</div>
<div style="margin-top:8px;color:#6b7684;font-size:11.6px">左：轨迹回放（结构按残基编号着色）｜右：上=主链 RMSD，下=各残基 CA 涨落</div>
</div>

<div class="card"><h2>这条蛋白为什么值得看动力学 <span>%(role)s</span></h2>
<p>%(domdesc)s</p>
<p style="margin-top:6px">柔性最大的残基（RMSF 前 8）：</p>
<table><thead><tr><th>残基</th><th style="text-align:right">RMSF (Å)</th></tr></thead><tbody>%(flex)s</tbody></table>
</div>

<div class="card"><h2>结果解读 <span>自动按本次轨迹生成，不预设结论</span></h2>
<ul style="margin:4px 0 0 18px;padding:0;font-size:12.6px">%(interp)s</ul>
</div>

<div class="card"><h2>方法与边界 <span>必须一起讲</span></h2>
<div class="warn">
<b>这是隐式溶剂 MD，不是全原子显式水模拟。</b>
<ul>
<li>溶剂用 GBSA-OBC2 连续介质近似，省去水分子与离子，速度快 10–50 倍，但<strong>不能</strong>用来判断结合水、离子桥、界面水网络；</li>
<li>时长 %(ns).2f ns，只能看局部构象涨落与早期形变，<strong>不能</strong>据此判断热稳定性排序或货架期；</li>
<li>起始结构为晶体结构（含缺失段由 pdbfixer 补齐），预测段与补齐段的运动学解释力弱；</li>
<li>单条轨迹、无重复，结论属“观察”，不属“统计显著”。</li>
</ul>
</div>
</div>

<div class="card"><h2>销售怎么用 <span>只说观察，不说保证</span></h2>
<ol>
<li><b>讲构象不是静止的</b>：酶在 300 K 下持续涨落，活性位点附近的柔性决定它“容不容易被温度/离子条件带偏”；</li>
<li><b>指到具体残基</b>：RMSF 高的环区往往是突变设计、耐热改造的着力点；</li>
<li><b>边界话术</b>：客户问“能不能证明更耐热”，回答用实测（半衰期、Tm、加速老化），不用 MD 动画代替。</li>
</ol>
</div>
%(footer)s
</div></body></html>""" % {
    'name': name, 'uniprot': uniprot, 'pdbid': pdbid, 'role': role, 'domdesc': domdesc,
    'ns': s['duration_ns'], 'atoms': s['atoms'], 'rmsd': s['rmsd_mean_A'], 'rmsf': s['rmsf_mean_A'],
    'frames': s['frames'], 'frame_ps': s['duration_ns'] * 1000 / max(1, s['frames'] - 1),
    'gif': gif, 'vid': vid, 'dirname': os.path.basename(d), 'curve': curve, 'flex': flex, 'footer': '__FOOTER__', 'interp': _interp_html,
}
page = BK.inject(page, theme='dark', tag='%s 分子动力学' % name,
                 footer_extra='内部资料 · 请勿外发 ｜ 隐式溶剂 MD，结论仅限构象涨落')
page = page.replace('__FOOTER__', '')
out = os.path.join(W, '销售分析', '分子动力学_%s_%s.html' % (name, pdbid))
io.open(out, 'w', encoding='utf-8').write(page)
print('✅', out, '%.1f MB' % (len(page.encode()) / 1048576))
