"""Build three editable one-pagers while preserving the CPT template package."""
from pathlib import Path
import hashlib, json, shutil, subprocess, sys, zipfile
from lxml import etree as E

ROOT = Path(__file__).resolve().parents[6]
HERE = Path(__file__).resolve().parent
OUT = ROOT / 'oss/local-deliverables/2026-09-23-AI智慧健康站一页纸'
SKILL = ROOT / 'standards-stack/agent-skills/skills/kangbite-word-template'
W = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
N = {'w': W}

def el(tag, **attrs):
    return E.Element('{%s}%s' % (W, tag), {'{%s}%s' % (W,k): str(v) for k,v in attrs.items()})

def p(text, size=21, bold=False, color='242A32', before=0, after=70, style=None):
    node=el('p'); props=el('pPr'); node.append(props)
    if style: props.append(el('pStyle', val=style))
    props.append(el('spacing', before=before, after=after, line=max(260,size*10+30), lineRule='exact'))
    props.append(el('snapToGrid', val=0))
    if bold: props.append(el('keepNext'))
    r=el('r'); rp=el('rPr'); r.append(rp)
    rp.append(el('rFonts', ascii='Arial', hAnsi='Arial', eastAsia='PingFang SC'))
    if bold: rp.append(el('b'))
    rp.append(el('color', val=color)); rp.append(el('sz', val=size)); rp.append(el('szCs', val=size))
    t=el('t'); t.text=text; r.append(t); node.append(r)
    return node

def heading(text): return p(text,24,True,'000000',140,60)

DOCS = [
dict(file='01_产品介绍一页纸', title='AI智慧健康站产品介绍', label='健康体检体测一体机 + AI 运动营养师', intro='以 AI Agent 为核心，把一次体检测量延伸为持续的运动与营养服务，让用户知道自己的身体状况、今天怎么动、每天怎么吃。', sections=[
('核心壁垒  将国家队服务经验转化为AI能力', [
'康比特将服务 100 支国家队、积累 25 年的运动营养经验用于 AI 训练，形成营养垂类大模型，为 AI 运动营养师提供专业知识与方案生成能力。',
'核心竞争力来自长期服务中沉淀的专业经验，以及对运动与营养相互关系的理解。AI Agent 将这些经验与个人健康数据结合，转化为更贴合个人目标的吃动建议。']),
('数据闭环  让方案跟着身体变化调整',[
'连接上百款硬件及运动健康应用生态，覆盖华为运动健康、苹果健康生态。在用户授权及接口支持范围内，汇集体测、运动和相关健康数据；营养摄入信息结合实际接入来源与用户记录补充。',
'检测与数据汇集 → AI 分析 → 运动方案与营养方案 → 执行反馈与复测 → 方案调整。用持续数据反馈支持下一轮建议，让测量、行动和复盘形成闭环。']),
('AI Agent为用户做什么',[
'理解状态：结合检测结果与个人目标，解释关键指标，明确需要关注的变化。',
'生成方案：将个人状态转化为运动安排和营养建议，帮助用户落实到日常吃动。',
'持续跟进：结合新增数据与执行反馈，追踪变化并更新建议。']),
('一体机是数据入口  AI是持续服务核心',[
'SH-V20 健康一体机支持身高、体重、血压、体成分检测及建档；AI 运动营养师通过小程序承接报告和个性化营养服务。适用于健身门店、健康管理机构、社区及企事业单位健康服务。'])],
closing='从了解身体开始，把专业运动营养服务带进每一天。',
note='内容确认稿｜25年、100支国家队、模型训练及百款接入口径由产品方提供，外发前需核对证明与接入清单。“国内独一无二”待补充可核验证据后使用。具体功能以交付版本为准；健康建议不替代医疗诊断。'),
dict(file='02_渠道产品介绍一页纸', title='AI智慧健康站渠道产品介绍', label='用AI持续服务承接到店检测与会员运营', intro='面向拥有门店、客户或机构资源的销售伙伴，提供“健康体检体测一体机 + AI 运动营养师”组合，帮助终端建立检测获客、持续服务和营养消费的业务链路。', sections=[
('渠道卖什么',[
'一套软硬件结合的营养健康服务产品：SH-V20 一体机完成检测建档，AI 运动营养师承接报告、运动与营养方案；康比特营养品作为按需选择的补充商品。']),
('为什么客户愿意持续使用',[
'专业积累：将服务 100 支国家队、25 年运动营养经验用于训练营养垂类大模型，为 AI Agent 提供专业基础。',
'连续数据：连接上百款硬件及华为运动健康、苹果健康生态，在授权和接口支持范围内，让门店检测与日常健康数据共同服务个人方案。',
'持续服务：AI Agent 把数据转化为吃动建议，再结合执行反馈和复测调整方案，为门店提供后续服务的切入点。']),
('帮渠道和门店建立三类收益机会',[
'设备销售：向有检测、建档及健康服务需求的客户销售设备，收益取决于成交价、供货价和交付成本。',
'会员服务：以报告解读、吃动跟进与复测促进会员互动和续用；服务内容、收费及分配方式另行约定。',
'营养品复购：结合真实需求提供适合的营养品选择，按授权商品及交易规则开展销售，不承诺固定复购或回本周期。']),
('优先向哪些客户销售',[
'健身与瑜伽连锁、健康管理机构、体检中心、社区健康服务点及企事业单位。优先选择已有稳定客群、能安排服务人员、愿意组织复测的客户。']),
('销售演示与合作路径',[
'先演示“一次检测 → AI报告与吃动方案 → 手机持续服务 → 复测反馈”，再共同确认设备配置、接入范围、运营分工和商务条件。以样板点实际使用验证合作价值。'])],
closing='合作沟通：提供目标客户、预计点位和服务场景，形成对应配置与报价。',
note='内容确认稿｜专业积累、模型与设备接入口径由产品方提供，待补齐证明。“买设备送软件”的功能、期限与服务边界以正式报价为准；不预设永久免费或收益保证。健康建议不替代医疗诊断。'),
dict(file='03_渠道政策一页纸_待确认草案', title='AI智慧健康站渠道政策', label='待确认草案  供合作洽谈与内部审批使用', intro='适用产品：健康体检体测一体机 + AI 运动营养师，可按需配置康比特营养品。以下合作机制均为拟议条款，未形成已生效的价格、授权或服务承诺。', sections=[
('合作对象与拟议分工',[
'面向具备客户资源、门店或机构拓展能力的渠道伙伴。拟由康比特提供约定产品与技术支持，渠道负责客户开发和本地服务协调；实际责任随项目书面确认。'])],
rows=[
('供货与销售','按设备配置、数量和交付范围单独报价。','供货价、建议零售价、含税及运费口径'),
('软件权益','可讨论设备随附基础软件服务。','赠送功能、账号数、有效期、续费与接口费用'),
('返利与结算','可按实际回款和约定业绩核算激励。','返利门槛、比例、周期、退款冲回与付款条件'),
('客户与区域','可讨论客户报备及项目协同机制。','报备有效期、归属判定、区域范围及是否独家'),
('样机与培训','可讨论样机演示、产品培训与销售素材支持。','样机折扣或押金、借用期限、培训次数及费用'),
('交付与售后','按项目确认安装、调试、验收及支持分工。','交期、安装费、质保期、响应时效及退换条件'),
('营养品合作','按授权品类及独立商品政策开展销售。','供货折扣、起订量、订单归属、履约及售后')],
tail=[('合作推进与生效',[
'意向沟通 → 场景与资源评估 → 配置及商务确认 → 签署协议 → 培训与交付 → 运营复盘。未经书面授予，不推定独家代理、区域保护、返利或免费服务权益。',
'拟议经营要求：使用确认后的宣传材料；数据接入须取得用户授权；不承诺诊疗效果、固定收益或回本周期。终止合作时的客户交接、未结款项和数据处理方式另行约定。'])],
closing='待确认负责人及日期：________________    正式政策版本：________________',
note='草案未生效｜金额、比例、期限与责任分工均待确认，以双方签署的协议、正式报价及有效政策附件为准。')
]

def table(rows):
    t=el('tbl'); pr=el('tblPr'); t.append(pr)
    pr.append(el('tblW', w=8306, type='dxa')); pr.append(el('tblLayout', type='fixed'))
    borders=el('tblBorders')
    for edge in ['top','left','bottom','right','insideH','insideV']:
        borders.append(el(edge,val='single',sz=4,color='D9D9D9'))
    pr.append(borders)
    mar=el('tblCellMar')
    for edge in ['top','bottom','left','right']: mar.append(el(edge,w=70,type='dxa'))
    pr.append(mar)
    grid=el('tblGrid'); widths=[1250,3380,3676]
    for width in widths: grid.append(el('gridCol',w=width))
    t.append(grid)
    for i,row in enumerate([('政策项','拟议机制','待确认内容')]+rows):
        tr=el('tr'); trpr=el('trPr'); trpr.append(el('cantSplit')); tr.append(trpr)
        for text,width in zip(row,widths):
            tc=el('tc'); cp=el('tcPr'); cp.append(el('tcW',w=width,type='dxa'))
            cp.append(el('vAlign',val='center'))
            cp.append(el('shd',val='clear',fill='123E70' if i==0 else ('F2F5F8' if i%2==0 else 'FFFFFF')))
            tc.append(cp); tc.append(p(text,19,i==0,'FFFFFF' if i==0 else '242A32',after=20));tr.append(tc)
        t.append(tr)
    return t

def main():
    OUT.mkdir(parents=True,exist_ok=True)
    qa=OUT/'qa'; qa.mkdir(exist_ok=True)
    shutil.copy2(SKILL/'references/template-spec.md',qa/'artifact.md')
    shutil.copy2(SKILL/'assets/kangbite-digital-sports-word-template.docx',qa/'reference.docx')
    subprocess.run([sys.executable,str(SKILL/'scripts/prepare_working_copy.py'),str(qa/'reference.docx'),str(qa/'starter.docx')],check=True)
    manifest=[]
    for data in DOCS:
        with zipfile.ZipFile(qa/'starter.docx') as z:
            parts={n:z.read(n) for n in z.namelist()}
        doc=E.fromstring(parts['word/document.xml']); body=doc.find('w:body',N)
        sect=body.find('w:sectPr',N)
        for child in list(body):
            if child is not sect: body.remove(child)
        nodes=[p(data['title'],36,True,'000000',after=100,style='Title'),p(data['label'],23,True,'000000',after=140),p(data['intro'],21,after=100)]
        for title,paras in data['sections']:
            nodes.append(heading(title));nodes.extend(p(s) for s in paras)
        if data.get('rows'):
            nodes.append(heading('商务与支持条款'))
            nodes.append(table(data['rows']))
        for title,paras in data.get('tail',[]):
            nodes.append(heading(title));nodes.extend(p(s,20) for s in paras)
        nodes.extend([p(data['closing'],20,True,'000000',140,80),p(data['note'],16,False,'606872',100,0)])
        for i,node in enumerate(nodes): body.insert(i,node)
        parts['word/document.xml']=E.tostring(doc,xml_declaration=True,encoding='UTF-8',standalone=True)
        dest=OUT/(data['file']+'.docx')
        with zipfile.ZipFile(dest,'w',zipfile.ZIP_DEFLATED) as z:
            for name,blob in parts.items(): z.writestr(name,blob)
        with zipfile.ZipFile(qa/'reference.docx') as ref, zipfile.ZipFile(dest) as final:
            for name in ref.namelist():
                if name!='word/document.xml': assert ref.read(name)==final.read(name),name
        manifest.append({'file':dest.name,'sha256':hashlib.sha256(dest.read_bytes()).hexdigest(),'preserved_template_parts':True})
        print(dest)
    (HERE/'content.json').write_text(json.dumps(DOCS,ensure_ascii=False,indent=2)+'\n')
    (qa/'manifest.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n')

if __name__=='__main__': main()
