import csv
import hashlib
import html
import json
import os
import subprocess
from pathlib import Path

from openpyxl import Workbook, load_workbook
from openpyxl.styles import Alignment, Font, PatternFill
from openpyxl.utils import get_column_letter

BASE = Path(__file__).resolve().parent
data = json.loads((BASE / 'research-data.json').read_text())
source = (BASE / 'personal-nutrition-research.md').read_text()
render_js = '''const fs=require('node:fs');const {marked}=require('marked');
let i=0;const toc=[];const renderer=new marked.Renderer();
renderer.heading=function(t){const s=this.parser.parseInline(t.tokens);if(t.depth===2){const id='section-'+(++i);toc.push({id,label:t.text});return `<h2 id="${id}">${s}</h2>`;}return `<h${t.depth}>${s}</h${t.depth}>`;};
const body=marked.parse(fs.readFileSync(process.argv[1],'utf8'),{renderer,gfm:true});process.stdout.write(JSON.stringify({body,toc}));'''
rendered = json.loads(subprocess.check_output(['node', '-e', render_js, str(BASE / 'personal-nutrition-research.md')], text=True))
body = rendered['body']
toc_html = '<ul class="toc">' + ''.join(f'<li><a href="#{h["id"]}">{html.escape(h["label"])}</a></li>' for h in rendered['toc']) + '</ul>'
body = body.replace('<table>', '<div class="table-scroll" tabindex="0"><table>').replace('</table>', '</table></div>')
css = '''
:root{--bg:#f5f7f7;--paper:#fff;--ink:#202a29;--muted:#556562;--line:#dce4e1;--accent:#be492a;--green:#17645a;--soft:#eef6f2;--radius:6px}
*{box-sizing:border-box}html{scroll-behavior:smooth;scroll-padding-top:24px}body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.85 "Microsoft YaHei","PingFang SC",system-ui,sans-serif;letter-spacing:0}
.top{background:var(--paper);border-bottom:1px solid var(--line)}.top-inner{max-width:1180px;margin:auto;padding:15px 24px;display:flex;justify-content:space-between;gap:16px;color:var(--muted);font-size:13px}.brand{color:var(--accent);font-weight:700}
.page{max-width:1180px;margin:auto;padding:32px 24px 60px}.kicker{font-size:13px;color:var(--green);font-weight:700;margin:0 0 12px}.reading-grid{display:grid;grid-template-columns:205px minmax(0,1fr);gap:38px}aside{position:sticky;top:22px;align-self:start;max-height:90vh;overflow:auto;border-top:3px solid var(--green);font-size:13px}aside ul{list-style:none;padding:0}aside li{padding:6px 0;border-bottom:1px solid var(--line)}aside li ul{display:none}aside a{color:var(--muted);text-decoration:none}
article{min-width:0}h1{font-size:34px;line-height:1.4;margin:0 0 22px;max-width:850px}h2{font-size:24px;line-height:1.45;margin:44px 0 18px;padding-top:22px;border-top:1px solid var(--line)}h3{font-size:19px;margin:28px 0 12px}p{margin:12px 0}a{color:var(--green);overflow-wrap:anywhere}strong{font-weight:700}blockquote{margin:20px 0;padding:14px 20px;border-left:4px solid var(--accent);background:var(--paper);color:var(--muted)}blockquote p{margin:0}ul,ol{padding-left:24px}li{margin:7px 0}
.table-scroll{overflow-x:auto;max-width:100%;margin:22px 0;background:var(--paper);border:1px solid var(--line)}table{border-collapse:collapse;width:100%;min-width:640px;font-size:14px;line-height:1.7}th,td{padding:12px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line);overflow-wrap:anywhere}th{background:var(--soft);color:var(--green);font-weight:700}tr:last-child td{border-bottom:0}code{font-size:13px;overflow-wrap:anywhere}pre{overflow:auto;background:var(--soft);padding:18px}footer{margin-top:42px;border-top:1px solid var(--line);padding-top:16px;font-size:13px;color:var(--muted)}.source-view{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:26px 0}.source-view img{width:100%;height:210px;object-fit:cover;object-position:top;border:1px solid var(--line)}figure{margin:0}figcaption{color:var(--muted);font-size:12px}
@media(max-width:850px){.reading-grid{display:block}aside{position:static;max-height:none;margin-bottom:28px}aside details:not([open]) .toc{display:none}.page{padding:24px 18px 40px}h1{font-size:28px}h2{font-size:22px}.top-inner{padding:12px 18px}.source-view{grid-template-columns:1fr}table{min-width:580px}}
@media print{aside,.top{display:none}.page{max-width:none;padding:0}.reading-grid{display:block}h2{break-after:avoid}.table-scroll{overflow:visible}table{min-width:0;font-size:10px}body{background:white;font-size:12px}.source-view{display:none}}
'''
evidence_dir = Path(os.environ.get('NUTRITION_EVIDENCE_DIR', str(BASE / 'local-evidence')))
figures = []
import base64
for name, caption in [('nutripacks-public.jpg', '橘袋公开网页截图；不代表已登录微信小程序。'), ('lemonbox-public.jpg', 'LemonBox公开测评入口；仅作为产品流程证据。')]:
    p = evidence_dir / name
    if p.exists() and os.environ.get('NUTRITION_EMBED_PUBLIC_EVIDENCE') == '1':
        figures.append(f'<figure><img src="data:image/jpeg;base64,{base64.b64encode(p.read_bytes()).decode()}" alt="{html.escape(caption)}"><figcaption>{html.escape(caption)}</figcaption></figure>')
gallery = '<section class="source-view">' + ''.join(figures) + '</section>' if figures else ''
page = f'''<!doctype html>
<html lang="zh-CN" data-theme="minimal-white"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>个人营养产品调研 · 橘袋 / AI / 可穿戴</title><style>{css}</style></head>
<body class="webpage"><header class="top"><div class="top-inner"><span class="brand">康比特 · 产品研究</span><span>2026.09.22 / 内部讨论</span></div></header>
<div class="page"><p class="kicker">PERSONAL NUTRITION / 产品模式与增长验证</p><div class="reading-grid"><aside><details><summary>研究目录</summary>{toc_html}</details></aside><article>{body}{gallery}<footer>正文源自同名Markdown。模板结构参考pakco-html/templates/webpage.html；报告没有连接真实健康数据、商品订单或竞品账户。</footer></article></div></div>
<script>document.querySelector('aside details').open=matchMedia('(min-width:851px)').matches;</script>
</body></html>'''
(BASE / 'personal-nutrition-research.html').write_text(page)

workbook = Workbook()
workbook.remove(workbook.active)
csv_names = ['source-evidence.csv', 'competitor-matrix.csv', 'segment-definition.csv', 'device-data-map.csv', 'journey-gates.csv', 'unit-economics-model.csv', 'validation-backlog.csv', 'source-refresh-audit.csv']
for (title, table), csv_name in zip(data['tables'].items(), csv_names):
    headers, rows = table['headers'], table['rows']
    with (BASE / csv_name).open('w', newline='', encoding='utf-8-sig') as stream:
        writer = csv.writer(stream)
        writer.writerow(headers)
        writer.writerows(rows)
    sheet = workbook.create_sheet(title)
    sheet.append(headers)
    for row in rows:
        assert len(row) == len(headers), (title, row)
        sheet.append(row)
    sheet.freeze_panes = 'B2'
    sheet.auto_filter.ref = sheet.dimensions
    sheet.sheet_view.showGridLines = False
    for row in sheet:
        for cell in row:
            cell.font = Font(name='微软雅黑', size=12, color='24332F')
            cell.alignment = Alignment(vertical='top', wrap_text=True)
            if cell.row == 1:
                cell.font = Font(name='微软雅黑', size=12, color='FFFFFF', bold=True)
                cell.fill = PatternFill('solid', fgColor='17645A')
            elif cell.row % 2 == 0:
                cell.fill = PatternFill('solid', fgColor='EEF5F2')
            if isinstance(cell.value, str) and cell.value.startswith('https://'):
                cell.hyperlink = cell.value
                cell.font = Font(name='微软雅黑', size=12, color='17645A', underline='single')
    for c in range(1, len(headers) + 1):
        sheet.column_dimensions[get_column_letter(c)].width = 21 if c == 1 else 40
    sheet.row_dimensions[1].height = 36
    for row in range(2, sheet.max_row + 1):
        sheet.row_dimensions[row].height = 94
    sheet.sheet_properties.pageSetUpPr.fitToPage = True
    sheet.page_setup.orientation = 'landscape'
    sheet.page_setup.paperSize = sheet.PAPERSIZE_A3
    sheet.page_setup.fitToWidth = 1
    sheet.page_setup.fitToHeight = 0
    sheet.print_title_rows = '1:1'
workbook.save(BASE / 'research-tables.xlsx')
readback = load_workbook(BASE / 'research-tables.xlsx', data_only=False)
for name, table in data['tables'].items():
    actual = list(readback[name].values)
    assert actual == [tuple(table['headers'])] + [tuple(r) for r in table['rows']], name
    assert not any(c.data_type in ('e', 'f') for row in readback[name] for c in row)
manifest = []
for name in [*csv_names, 'research-tables.xlsx']:
    content = (BASE / name).read_bytes()
    manifest.append({'path': name, 'bytes': len(content), 'sha256': hashlib.sha256(content).hexdigest(), 'storage': 'local-reproducible-not-published'})
(BASE / 'asset-manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n')
print(json.dumps({'html_bytes': len(page.encode()), 'sheets': {n: len(t['rows']) for n, t in data['tables'].items()}, 'workbook_readback': 'PASS', 'formulas': 'none; methodological expressions are text'}, ensure_ascii=False))
