#!/usr/bin/env python3
"""Build management HTML and Excel for the 12-SKU smart-canteen strategy."""

from __future__ import annotations

import argparse
import html
import json
import re
import shutil
import tempfile
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

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


NAVY = "14354B"
ORANGE = "F57C00"
AMBER = "FFA51F"
LIGHT_ORANGE = "FFF1DF"
GREEN = "218C74"
BLUE = "0B79B7"
RED = "C0392B"
GRAY = "E8EEF2"
LIGHT_GRAY = "F5F7F9"
WHITE = "FFFFFF"
BLACK = "222222"
THIN = Side(style="thin", color="CAD4DB")


def title(ws, text, subtitle, width):
    ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=width)
    ws.cell(1, 1, text)
    ws.cell(1, 1).fill = PatternFill("solid", fgColor=NAVY)
    ws.cell(1, 1).font = Font(name="微软雅黑", size=18, bold=True, color=WHITE)
    ws.cell(1, 1).alignment = Alignment(vertical="center")
    ws.row_dimensions[1].height = 34
    ws.merge_cells(start_row=2, start_column=1, end_row=2, end_column=width)
    ws.cell(2, 1, subtitle)
    ws.cell(2, 1).fill = PatternFill("solid", fgColor=LIGHT_ORANGE)
    ws.cell(2, 1).font = Font(name="微软雅黑", size=10, color=BLACK)
    ws.cell(2, 1).alignment = Alignment(wrap_text=True, vertical="center")
    ws.row_dimensions[2].height = 32


def header(ws, row, values, fill=BLUE):
    for col, value in enumerate(values, 1):
        cell = ws.cell(row, col, value)
        cell.fill = PatternFill("solid", fgColor=fill)
        cell.font = Font(name="微软雅黑", size=10, bold=True, color=WHITE)
        cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
        cell.border = Border(top=THIN, bottom=THIN, left=THIN, right=THIN)
    ws.row_dimensions[row].height = 30


def body(ws, first, last, cols):
    for row in ws.iter_rows(min_row=first, max_row=last, min_col=1, max_col=cols):
        for cell in row:
            cell.font = Font(name="微软雅黑", size=10, color=BLACK)
            cell.alignment = Alignment(vertical="center", wrap_text=True)
            cell.border = Border(top=THIN, bottom=THIN, left=THIN, right=THIN)


def widths(ws, values):
    for idx, value in enumerate(values, 1):
        ws.column_dimensions[get_column_letter(idx)].width = value


def setup(ws, a3=False, portrait=False):
    ws.sheet_view.showGridLines = False
    ws.page_setup.orientation = "portrait" if portrait else "landscape"
    ws.page_setup.paperSize = ws.PAPERSIZE_A3 if a3 else ws.PAPERSIZE_A4
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 0
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_margins.left = 0.25
    ws.page_margins.right = 0.25
    ws.page_margins.top = 0.45
    ws.page_margins.bottom = 0.45
    ws.oddFooter.center.text = "CPT｜内部战略评审稿｜目标、评分和漏斗须用真实经营数据校准"
    ws.oddFooter.center.size = 8


def inject_cache(path: Path, cache_maps):
    with ZipFile(path, "r") as src:
        members = {name: src.read(name) for name in src.namelist()}
    for member, cells in cache_maps.items():
        xml = members[member].decode("utf-8")
        for ref, value in cells.items():
            text = str(int(value) if float(value).is_integer() else float(value))
            pattern = re.compile(rf'(<c r="{re.escape(ref)}"[^>]*>.*?<f>.*?</f><v>).*?(</v></c>)')
            xml, count = pattern.subn(rf'\g<1>{text}\g<2>', xml, count=1)
            if count != 1:
                raise RuntimeError(f"cache cell missing: {member} {ref}")
        members[member] = xml.encode("utf-8")
    with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False, dir=path.parent) as tmp:
        temp_path = Path(tmp.name)
    try:
        with ZipFile(temp_path, "w", ZIP_DEFLATED) as out:
            for name, data in members.items():
                out.writestr(name, data)
        temp_path.replace(path)
    finally:
        if temp_path.exists():
            temp_path.unlink()


def weighted_score(sku, weights):
    return (
        sku["market"] * weights["market_attractiveness"]
        + sku["position"] * weights["competitive_position"]
        + sku["finance"] * weights["financial_potential"]
        + sku["delivery"] * weights["delivery_repeatability"]
    )


def build_excel(model, out_path):
    wb = Workbook()
    ws = wb.active
    ws.title = "00-战略总览"
    title(ws, "智慧食堂前厅12个SKU｜年度产品与销售战略", f"规划周期：{model['planning_period']}。本工作簿是内部评审模型，不是已批准预算或对外市场份额声明。", 8)
    overview = [
        ("方法", "产品地图 → 市场细分 → SPAN/APPEALS/PDC → 7-2-1 → 年度任务 → 季度战役"),
        ("一句话战略", "先用央国企/机关的补贴、对账、称重和审计刚需进入，再用营养健康与智厨形成差异化溢价。"),
        ("主战SKU", "SKU02多档口多食堂、SKU04补贴与多策略、SKU05绑盘称重"),
        ("增购SKU", "SKU01单食堂入口、SKU08移动订餐、SKU10电子菜牌"),
        ("突破SKU", "SKU03集团版、SKU09 K12、SKU12营养健康智厨专业版"),
        ("布局SKU", "SKU06免绑盘、SKU07 AI视觉、SKU11集中保障"),
        ("基准签约目标", model["recommended_target_wan"]),
        ("回款目标", model["collection_target_wan"]),
        ("毛管道目标", model["gross_pipeline_target_wan"]),
        ("主项目胜单", model["primary_win_target"]),
        ("证据边界", model["assumption_note"]),
    ]
    row = 4
    for label, value in overview:
        ws.cell(row, 1, label); ws.merge_cells(start_row=row, start_column=2, end_row=row, end_column=8); ws.cell(row, 2, value)
        ws.cell(row, 1).fill = PatternFill("solid", fgColor=GRAY)
        for c in range(1, 9):
            ws.cell(row, c).border = Border(top=THIN, bottom=THIN, left=THIN, right=THIN)
            ws.cell(row, c).font = Font(name="微软雅黑", size=10, bold=c == 1)
            ws.cell(row, c).alignment = Alignment(wrap_text=True, vertical="center")
        if label in {"基准签约目标", "回款目标", "毛管道目标"}:
            ws.cell(row, 2).number_format = '0"万元"'
            ws.cell(row, 2).font = Font(name="微软雅黑", size=15, bold=True, color=ORANGE)
        row += 1
    widths(ws, [18, 22, 22, 22, 22, 22, 22, 22]); setup(ws, portrait=True)

    pws = wb.create_sheet("01-12SKU产品地图")
    title(pws, "12个SKU产品地图与战略角色", "不是按12个产品平均分资源，而是按主战、入口/增购、突破、布局形成产品关系。", 10)
    ph = ["序号", "SKU编码", "SKU名称", "战略角色", "主战场", "年度目标(万元)", "目标单量", "规划客单价(万元)", "产品动作", "证据等级"]
    header(pws, 4, ph)
    for r, sku in enumerate(model["skus"], 5):
        vals = [sku["sequence"], sku["code"], sku["name"], sku["role"], sku["battle"], sku["target_wan"], sku["target_units"], sku["planning_asp_wan"], sku["action"], "A/B能力证据；D级目标"]
        for c, value in enumerate(vals, 1): pws.cell(r, c, value)
        pws.row_dimensions[r].height = 56
    body(pws, 5, 16, 10); widths(pws, [7, 15, 24, 14, 34, 15, 11, 16, 48, 21]); pws.freeze_panes = "A5"; pws.auto_filter.ref = "A4:J16"; setup(pws, a3=True)

    sws = wb.create_sheet("02-SPAN-PDC")
    title(sws, "12个SKU SPAN/PDC初始评分", "1—5分；权重：市场吸引力35%、竞争地位30%、财务潜力20%、交付复用15%。全部分数为D级假设，需跨部门评分会确认。", 11)
    sh = ["序号", "SKU编码", "SKU名称", "战略角色", "市场吸引力", "竞争地位", "财务潜力", "交付复用", "PDC加权分", "排序", "关键动作"]
    header(sws, 4, sh)
    score_cache = {}
    rank_data = sorted([(weighted_score(x, model["score_weights"]), x["code"]) for x in model["skus"]], reverse=True)
    ranks = {code: idx + 1 for idx, (_, code) in enumerate(rank_data)}
    for r, sku in enumerate(model["skus"], 5):
        vals = [sku["sequence"], sku["code"], sku["name"], sku["role"], sku["market"], sku["position"], sku["finance"], sku["delivery"], None, ranks[sku["code"]], sku["action"]]
        for c, value in enumerate(vals, 1): sws.cell(r, c, value)
        sws.cell(r, 9, f"=E{r}*35%+F{r}*30%+G{r}*20%+H{r}*15%")
        score_cache[f"I{r}"] = weighted_score(sku, model["score_weights"])
        sws.cell(r, 9).number_format = "0.00"
        sws.row_dimensions[r].height = 50
    body(sws, 5, 16, 11); widths(sws, [7, 15, 24, 14, 13, 13, 13, 13, 14, 9, 52]); sws.freeze_panes = "A5"; sws.auto_filter.ref = "A4:K16"; setup(sws, a3=True)

    rws = wb.create_sheet("03-7-2-1资源配置")
    title(rws, "7-2-1产品、区域与渠道资源配置", "7-2-1表示优先级，不机械等于人数或预算比例；资源必须有目标、动作、升级与退出条件。", 8)
    rh = ["层级", "包含SKU/对象", "资源建议", "年度签约目标", "销售动作", "产品/交付动作", "升级条件", "退出/降级条件"]
    header(rws, 4, rh, fill=GREEN)
    rows = [
        ["聚焦7", "SKU01/02/04/05/08/10", "约70%销售、售前和标准化资源", 560, "央国企/机关员工餐与称重餐线主战；存量增购", "冻结标准包、设备白名单、报价和验收", "同包持续复制、回款与毛利达标", "出现大比例定制或低价无毛利时收缩"],
        ["突破2", "SKU03/09/12", "约20%资源，每个方向只给1个付费样板", 160, "已有客户向集团升级；伙伴进入K12；专业版树标杆", "补多组织、接口、持续服务和案例证据", "同包3个付费客户且标准复用成立", "无付费方/渠道/持续服务证据则停止加码"],
        ["布局1", "SKU06/07/11", "约10%资源，只做付费验证和机会投标", 80, "不主动大范围推广", "真机压测、异常恢复、离线与安全验收", "两个季度内形成2个付费样板", "无样板、交付失控或需大量定制则降级"],
        ["聚焦区域", "京津冀、长三角", "优先账户和案例资源", 0, "围绕现有项目密度做样板集群", "形成可参观、可引用案例", "每区至少3个样板", "连续两季无有效商机则换区"],
        ["突破渠道", "央国企集成商、银行/教育伙伴、团餐运营商", "伙伴联合方案与联合投标", 0, "伙伴控制入口，我们提供标准产品与交付Proof", "接口包、伙伴报价和边界标准化", "每类至少1个有效商机和1个胜单", "只要底价、不给客户与数据则淘汰"],
    ]
    for r, vals in enumerate(rows, 5):
        for c, value in enumerate(vals, 1): rws.cell(r, c, value)
        rws.row_dimensions[r].height = 70
    body(rws, 5, 9, 8); widths(rws, [14, 28, 28, 16, 42, 42, 36, 38]); setup(rws, a3=True)

    aws = wb.create_sheet("04-年度销售任务")
    title(aws, "未来12个月销售任务分解", "推荐基准800万元；规划客单价和单量是内部假设，不能替代真实商机预测。", 9)
    ah = ["序号", "SKU编码", "SKU名称", "战略角色", "目标单量", "规划客单价(万元)", "签约目标(万元)", "占比", "备注"]
    header(aws, 4, ah, fill=ORANGE)
    sales_cache = {}
    for r, sku in enumerate(model["skus"], 5):
        vals = [sku["sequence"], sku["code"], sku["name"], sku["role"], sku["target_units"], sku["planning_asp_wan"], None, None, sku["battle"]]
        for c, value in enumerate(vals, 1): aws.cell(r, c, value)
        aws.cell(r, 7, f"=E{r}*F{r}")
        aws.cell(r, 8, f"=G{r}/$G$17")
        sales_cache[f"G{r}"] = sku["target_wan"]
        sales_cache[f"H{r}"] = sku["target_wan"] / model["recommended_target_wan"]
        aws.cell(r, 8).number_format = "0.0%"
        aws.row_dimensions[r].height = 42
    aws.cell(17, 1, "合计"); aws.merge_cells("A17:F17"); aws.cell(17, 7, "=SUM(G5:G16)"); aws.cell(17, 8, "=SUM(H5:H16)")
    sales_cache["G17"] = model["recommended_target_wan"]; sales_cache["H17"] = 1
    body(aws, 5, 17, 9); widths(aws, [7, 15, 24, 14, 12, 18, 18, 12, 42]); aws.freeze_panes = "A5"; setup(aws, a3=True)

    fws = wb.create_sheet("05-销售漏斗")
    title(fws, "800万元基准情景｜销售漏斗", "管道覆盖目标3倍；阶段概率是冷启动假设，每月必须用真实赢单、失单和延期结果校准。", 7)
    fh = ["阶段", "目标数量", "阶段转化率", "必须证据", "管理动作", "停止条件", "责任角色"]
    header(fws, 4, fh)
    for r, item in enumerate(model["funnel"], 5):
        vals = [item["stage"], item["count"], item["conversion"], item["evidence"], "明确下一步、负责人和日期", "缺客户/预算/范围/证据则不得升级阶段", "销售主责，产品/交付/商务补Proof"]
        for c, value in enumerate(vals, 1): fws.cell(r, c, value)
        fws.cell(r, 3).number_format = "0%"
        fws.row_dimensions[r].height = 54
    body(fws, 5, 10, 7); widths(fws, [20, 14, 15, 40, 36, 38, 36]); setup(fws, a3=True)

    qws = wb.create_sheet("06-季度战役")
    title(qws, "未来12个月季度战役", "先建制与样板，再同包复制、区域突破和持续收入；每季度重做SPAN/PDC。", 6)
    qh = ["阶段", "签约目标(万元)", "胜单目标", "战役主题", "关键动作", "通过门槛"]
    header(qws, 4, qh, fill=GREEN)
    for r, item in enumerate(model["quarters"], 5):
        vals = [item["period"], item["target_wan"], item["wins"], item["theme"], item["actions"], "目标、回款、标准复用和交付产能同时复盘"]
        for c, value in enumerate(vals, 1): qws.cell(r, c, value)
        qws.row_dimensions[r].height = 72
    qws.cell(9, 1, "年度合计"); qws.cell(9, 2, "=SUM(B5:B8)"); qws.cell(9, 3, "=SUM(C5:C8)")
    quarter_cache = {"B9": model["recommended_target_wan"], "C9": model["primary_win_target"]}
    body(qws, 5, 9, 6); widths(qws, [20, 18, 14, 22, 70, 44]); setup(qws, a3=True)

    bws = wb.create_sheet("07-战斗编组")
    title(bws, "五支市场战斗编组", "销售不直接展示12个SKU目录；先讲客户要解决的结果，再用SKU完成组合与升级。", 6)
    bh = ["战斗编组", "SKU组合", "购买委员会", "市场楔子", "进入路径", "不能承诺"]
    header(bws, 4, bh, fill=ORANGE)
    forbids = ["不免费包含无限接口和定制", "不卖裸设备，不承诺无条件吞吐", "集团版不等于无限组织和报表", "没有付费方与伙伴不投入区域平台", "离线与安全未验收不承诺全场景"]
    for r, (item, forbid) in enumerate(zip(model["battle_packages"], forbids), 5):
        vals = [item["name"], item["skus"], item["buyer"], item["wedge"], item["route"], forbid]
        for c, value in enumerate(vals, 1): bws.cell(r, c, value)
        bws.row_dimensions[r].height = 78
    body(bws, 5, 9, 6); widths(bws, [30, 28, 38, 45, 42, 40]); setup(bws, a3=True)

    kws = wb.create_sheet("08-KPI与停止线")
    title(kws, "年度经营KPI与停止线", "不能只看签约额；必须同时看回款、标准复用、交付、增购和持续收入。", 6)
    kh = ["维度", "指标", "基准目标", "证据来源", "触发纠偏", "停止/降级条件"]
    header(kws, 4, kh, fill=RED)
    kpis = [
        ["财务", "签约/回款", "800万/650万", "合同、回款", "连续2月低于季度节奏", "低价成交但不能证明毛利和回款"],
        ["管道", "毛管道覆盖", "≥3倍，即2400万", "商机台账", "低于1.5倍立即补管道", "无客户、金额、阶段和下一步不进预测"],
        ["产品", "标准+配置项占比", "逐季提高，先建立基线", "配置/定制分类", "非标持续增加", "同类需求仍需新分支则暂停扩张"],
        ["报价", "标准报价周期", "≤2个工作日", "报价时间戳", "空价、接口和部署未冻结", "未评审报价不得对外"],
        ["销售", "主项目胜单", "22个", "合同", "报价阶段赢单率低于25%", "连续两季没有同包复制"],
        ["增购", "SKU08/10增购覆盖", "≥40%主战项目产生机会", "增购商机", "只卖一次性硬件", "无增购/续费则重做产品组合"],
        ["交付", "标准项目周期", "内部目标≤30天", "项目计划/验收", "接口和非标拖期", "交付产能成为SOM上限"],
        ["突破", "SKU03/09/12", "每方向1个付费样板", "合同/验收", "无付费方、无复制", "同包3客户门槛无法接近则不加码"],
        ["布局", "SKU06/07/11", "两个季度内2个付费样板", "合同/测试", "真机或安全门禁失败", "无样板即降级/退出"],
        ["持续收入", "云/维保/营养服务", "至少3次续费/增购/扩站点验证", "续费合同", "合同仍只有一次性交付", "不能证明持续收入则不称第二曲线"],
    ]
    for r, vals in enumerate(kpis, 5):
        for c, value in enumerate(vals, 1): kws.cell(r, c, value)
        kws.row_dimensions[r].height = 58
    body(kws, 5, 14, 6); widths(kws, [14, 30, 32, 28, 38, 44]); setup(kws, a3=True)

    ews = wb.create_sheet("09-证据与缺口")
    title(ews, "证据等级、反方观点与补数计划", "书籍与公开市场资料只能定义方法和趋势；正式年度任务必须由公司经营数据确认。", 6)
    eh = ["证据/缺口", "当前事实", "等级", "支持什么", "不能证明什么", "下一动作"]
    header(ews, 4, eh)
    evidence = [
        ["康比特2025年报", "数字化体育科技服务1936.87万元、成本1062.67万元、毛利率45.13%；智慧餐厅11个项目验收", "A", "公司业务基础和项目落地", "智慧食堂单独收入、12SKU毛利", "财务拆出智慧食堂收入成本"],
        ["团餐收入样本", "11条记录、295.145万元、平均26.83万元、中位20.25万元", "B", "客单价与机关/国企项目线索", "完整年度收入和回款", "补成本、回款、渠道与失单"],
        ["政策", "机关企事业单位率先建设营养健康食堂；2024新增健康场所1万多家", "C", "营养健康方向有政策支持", "数字化采购预算和赢单", "锁具体地区、预算和采购人"],
        ["竞品官网", "头部已覆盖结算、订餐、称重、AI、食安、进销存和集团", "C", "通用功能高度同质化", "真实份额和项目质量", "按同一预算和招标项目做对比"],
        ["销售目标", "500/800/1200万元三情景", "D", "内部讨论和资源测算", "正式预算与可实现性", "用真实商机和产能复算"],
        ["成本毛利", "12个SKU缺完整成本、交付和售后工时", "Gap", "—", "PDC财务评分和价格红线", "财务/交付逐项目补数"],
        ["渠道漏斗", "缺最近12个月完整线索、报价、失单、签约", "Gap", "—", "管道覆盖与赢率", "销售建立统一商机台账"],
        ["持续收入", "缺SKU12营养服务续费与使用证据", "Gap", "—", "专业版是否可成为突破产品", "旗舰样板约定激活、参与和续费"],
    ]
    for r, vals in enumerate(evidence, 5):
        for c, value in enumerate(vals, 1): ews.cell(r, c, value)
        ews.row_dimensions[r].height = 68
    body(ews, 5, 12, 6); widths(ews, [24, 58, 10, 36, 42, 38]); setup(ews, a3=True)

    wb.calculation.calcMode = "auto"
    wb.calculation.fullCalcOnLoad = True
    wb.calculation.forceFullCalc = True
    wb.properties.title = "智慧食堂前厅12个SKU年度产品与销售战略"
    wb.properties.creator = "康比特数字体育科技"
    out_path.parent.mkdir(parents=True, exist_ok=True)
    wb.save(out_path)
    inject_cache(
        out_path,
        {
            "xl/worksheets/sheet3.xml": score_cache,
            "xl/worksheets/sheet5.xml": sales_cache,
            "xl/worksheets/sheet7.xml": quarter_cache,
        },
    )


def build_html(model, out_path):
    focus = [x for x in model["skus"] if x["role"].startswith("聚焦")]
    breakthrough = [x for x in model["skus"] if x["role"].startswith("突破")]
    layout = [x for x in model["skus"] if x["role"].startswith("布局")]
    def sku_cards(items):
        return "".join(f'<article class="sku"><span>{html.escape(x["role"])}</span><h3>{x["code"]}<br>{html.escape(x["name"])}</h3><b>{x["target_wan"]}万元</b><p>{html.escape(x["action"])}</p></article>' for x in items)
    battle = "".join(f'<article class="battle"><h3>{html.escape(x["name"])}</h3><div class="codes">{html.escape(x["skus"])}</div><p><b>购买委员会：</b>{html.escape(x["buyer"])}</p><p><b>楔子：</b>{html.escape(x["wedge"])}</p><p><b>路径：</b>{html.escape(x["route"])}</p></article>' for x in model["battle_packages"])
    quarters = "".join(f'<tr><td>{html.escape(x["period"])}</td><td>{x["target_wan"]}万元</td><td>{x["wins"]}个</td><td>{html.escape(x["theme"])}</td><td>{html.escape(x["actions"])}</td></tr>' for x in model["quarters"])
    page = f"""<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>智慧食堂12个SKU年度产品与销售战略</title><style>
    :root{{--orange:#f57c00;--amber:#ffa51f;--navy:#14354b;--ink:#26343d;--muted:#667782;--paper:#fffaf2}}*{{box-sizing:border-box}}body{{margin:0;background:var(--paper);color:var(--ink);font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;line-height:1.65}}header{{padding:64px 6vw 52px;background:linear-gradient(135deg,#ff8500,#ffb22e);color:#fff}}header small{{font-weight:800;letter-spacing:.12em}}h1{{font-size:46px;line-height:1.15;margin:16px 0}}header p{{max-width:900px;font-size:19px}}.metrics{{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:30px}}.metric{{background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.4);padding:18px;border-radius:16px}}.metric b{{font-size:28px;display:block}}main{{width:min(1240px,92vw);margin:auto;padding:44px 0 80px}}section{{margin:44px 0}}h2{{font-size:30px;color:var(--navy);margin-bottom:10px}}.lead{{font-size:18px;color:#4a5c66;max-width:1000px}}.decision{{background:#fff;border-left:7px solid var(--orange);padding:26px;border-radius:0 18px 18px 0;box-shadow:0 8px 30px rgba(80,45,0,.08);font-size:21px}}.grid{{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}}.sku,.battle{{background:#fff;border:1px solid #ffdaa1;border-radius:18px;padding:20px;box-shadow:0 7px 24px rgba(90,50,0,.06)}}.sku span{{color:#b85d00;background:#fff0d5;padding:4px 9px;border-radius:999px;font-size:13px}}.sku h3{{min-height:52px}}.sku b{{font-size:25px;color:var(--orange)}}.sku p,.battle p{{color:#52636d}}.battle .codes{{color:var(--orange);font-weight:800}}table{{width:100%;border-collapse:collapse;background:#fff;border-radius:16px;overflow:hidden}}th{{background:var(--navy);color:#fff;text-align:left}}th,td{{padding:14px;border-bottom:1px solid #e1e8ec;vertical-align:top}}.war{{background:var(--navy);color:#fff;border-radius:22px;padding:30px}}.war h2{{color:#fff}}.war ol{{columns:2;column-gap:50px}}.sources{{font-size:14px;color:var(--muted)}}footer{{background:#1d2d37;color:#dce5ea;padding:28px 6vw}}@media(max-width:900px){{.grid,.metrics{{grid-template-columns:1fr 1fr}}h1{{font-size:36px}}}}@media(max-width:650px){{.grid,.metrics{{grid-template-columns:1fr}}.war ol{{columns:1}}}}
    </style></head><body><header><small>CPT · PRODUCT STRATEGY</small><h1>12个SKU，不是12支散兵<br>而是一套市场作战体系</h1><p>依据《产品战略规划》的产品地图、SPAN/PDC与7-2-1方法，把前厅12个SKU重组为主战、增购、突破和验证四类角色。</p><div class="metrics"><div class="metric"><b>{model['recommended_target_wan']}万元</b>基准签约目标</div><div class="metric"><b>{model['collection_target_wan']}万元</b>回款假设</div><div class="metric"><b>{model['gross_pipeline_target_wan']}万元</b>毛管道要求</div><div class="metric"><b>{model['primary_win_target']}个</b>主项目胜单</div></div></header><main>
    <section><h2>战略选择</h2><div class="decision">不与头部全栈厂商拼功能数量和最低价。先打央国企、机关和大型企业食堂的补贴、对账、称重与审计刚需，再用营养健康和智厨建立康比特的差异化溢价。</div></section>
    <section><h2>聚焦7｜承担560万元</h2><p class="lead">SKU02/04/05是主战，SKU01负责低门槛进入，SKU08/10负责增购。资源优先用于标准报价、设备白名单、接口包、实施和验收Proof。</p><div class="grid">{sku_cards(focus)}</div></section>
    <section><h2>突破2｜承担160万元</h2><p class="lead">集团、K12和营养健康专业版每个方向只给一个付费样板；同包3个付费客户且标准复用成立后才加码。</p><div class="grid">{sku_cards(breakthrough)}</div></section>
    <section><h2>布局1｜承担80万元</h2><p class="lead">免绑盘、AI视觉、集中保障只做付费验证和机会型投标。两个季度没有样板，或者真机/安全/交付门禁失败，就降级。</p><div class="grid">{sku_cards(layout)}</div></section>
    <section><h2>五支战斗编组</h2><div class="grid">{battle}</div></section>
    <section><h2>未来12个月战役节奏</h2><table><thead><tr><th>阶段</th><th>目标</th><th>胜单</th><th>主题</th><th>关键动作</th></tr></thead><tbody>{quarters}</tbody></table></section>
    <section class="war"><h2>怎么打</h2><ol><li>建立200个实名目标账户，不做泛流量。</li><li>京津冀与长三角形成样板集群。</li><li>保持3倍毛管道，低于1.5倍立即补线索。</li><li>每周商机作战室只解决阻塞证据。</li><li>现有客户优先增购订餐、菜牌、集团和营养服务。</li><li>接口、部署、非标和施工单列，不用软件价兜底。</li><li>用回款、毛利、复用和持续收入约束签约额。</li><li>季度重做SPAN/PDC，布局产品可退出。</li></ol></section>
    <section><h2>三档年度任务</h2><table><tr><th>保底</th><th>基准</th><th>冲刺</th></tr><tr><td>{model['floor_target_wan']}万元</td><td>{model['recommended_target_wan']}万元</td><td>{model['stretch_target_wan']}万元</td></tr><tr><td>只做存量与高确定性项目</td><td>200账户、2400万管道、22个胜单</td><td>渠道与交付产能同步扩张后启用</td></tr></table></section>
    <section class="sources"><h2>证据边界</h2><p>{html.escape(model['assumption_note'])}</p><p>关键公开来源：<a href="https://www.bse.cn/disclosure/2026/2026-04-24/8349c08abb8e479abe10cf940ece8bfd.pdf">康比特2025年报</a> · <a href="https://www.nhc.gov.cn/sps/c100088/202012/9f71d532e5684a54a63090a75eb737fb.shtml">营养健康食堂建设指南</a> · <a href="https://www.nhc.gov.cn/wjw/jiany/202507/3e0d4370402f4b07b77fc3c28cd9c61c.shtml">国家卫健委2025年答复</a> · <a href="https://sovell.com/index.html">雄伟科技</a> · <a href="https://www.mankebao.com/html/product/">满客宝</a></p></section>
    </main><footer>内部评审稿｜正式销售目标需由经营、销售、财务、产品和交付共同确认。</footer></body></html>"""
    out_path.write_text(page, encoding="utf-8")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--workdir", type=Path, required=True)
    parser.add_argument("--output", type=Path, required=True)
    args = parser.parse_args()
    model = json.loads((args.workdir / "strategy-model.json").read_text(encoding="utf-8"))
    final_dir = args.output / "01-最终交付"
    evidence_dir = args.output / "02-内部证据"
    final_dir.mkdir(parents=True, exist_ok=True)
    evidence_dir.mkdir(parents=True, exist_ok=True)
    shutil.copy2(args.workdir / "SMART_CANTEEN_12_SKU_STRATEGY.md", final_dir / "智慧食堂12个SKU年度产品与销售战略.md")
    shutil.copy2(args.workdir / "strategy-model.json", evidence_dir / "strategy-model.json")
    shutil.copy2(args.workdir / "source-index.csv", evidence_dir / "source-index.csv")
    shutil.copy2(args.workdir / "ROUTING_NOTE.md", evidence_dir / "ROUTING_NOTE.md")
    build_excel(model, final_dir / "智慧食堂12个SKU年度产品与销售战略.xlsx")
    build_html(model, final_dir / "smart-canteen-12-sku-annual-strategy.html")
    print(json.dumps({"status":"PASS","output":str(args.output),"sku_count":len(model["skus"]),"target_wan":model["recommended_target_wan"]},ensure_ascii=False))


if __name__ == "__main__":
    main()
