#!/usr/bin/env python3
from __future__ import annotations

import csv
import html
import re
import shutil
import subprocess
import zipfile
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path

from docx import Document
from docx.enum.section import WD_ORIENTATION
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Pt, RGBColor


REPO_ROOT = Path("/Users/jack/code/010-cpt/008-zhct/zhctprompt")
PROJECT_DIR = REPO_ROOT / "modules/presales-bidding/customer-projects/rehab-aids-weighing-system-20260614"
WORK_DIR = REPO_ROOT / "work/2026-06-14-rehab-aids-weighing-bid-chatgpt-pack"
DESKTOP_PACK = Path("/Users/jack/Desktop/rehab-aids-weighing-system-bid-chatgpt-pack-20260614")

DELIVERABLE_DIR = PROJECT_DIR / "deliverables"
REVIEW_DIR = PROJECT_DIR / "review"
MATRIX_DIR = PROJECT_DIR / "response-matrix"
SOURCE_DIR = PROJECT_DIR / "source-docs"

PROJECT_NAME = "智慧餐厅称重系统改造"
PURCHASER = "北京蓝色港湾科技有限责任公司"
SUPPLIER = "北京康比特运动营养科技有限公司（待最终投标主体确认）"
DOC_DATE = "2026年6月"
VERSION = "正式长版编制稿 V1.0"


@dataclass
class RequirementItem:
    code: str
    category: str
    title: str
    requirement: str
    response: str
    implementation: str
    acceptance: str
    evidence: str


def read_csv(path: Path) -> list[list[str]]:
    with path.open(encoding="utf-8-sig", newline="") as f:
        return list(csv.reader(f))


def read_csv_dict(path: Path) -> list[dict[str, str]]:
    with path.open(encoding="utf-8-sig", newline="") as f:
        return list(csv.DictReader(f))


def split_numbered_items(text: str) -> list[str]:
    normalized = re.sub(r"\s+", " ", text).strip()
    parts = re.split(r"\s*/\s*(?=\d+[.、])", normalized)
    out: list[str] = []
    for idx, part in enumerate(parts, start=1):
        part = part.strip(" /")
        if not part:
            continue
        if idx == 1 and not re.match(r"^\d+[.、]", part):
            part = "1." + part
        out.append(part)
    return out


def item_title(text: str) -> str:
    text = re.sub(r"^\d+[.、]\s*", "", text).strip()
    title = re.split(r"[：:，,；;。]", text, maxsplit=1)[0].strip()
    return title[:28] if title else text[:28]


def classify_implementation(category: str, requirement: str) -> tuple[str, str, str]:
    r = requirement
    if any(k in r for k in ["检测报告", "检验报告", "软著", "著作权", "截图", "加盖公章"]):
        evidence = "需在正式附件中提供对应证书、检测报告、检验报告、功能截图或加盖公章证明材料。"
    elif any(k in r for k in ["CPU", "Android", "内存", "存储", "液晶屏", "摄像头", "读卡器", "扫码器", "电子秤", "RJ45"]):
        evidence = "需提供最终设备品牌型号、厂家参数页、产品彩页或厂家授权文件。"
    else:
        evidence = "可通过系统功能说明、实施记录、测试记录、截图和验收记录支撑。"

    if "PC" in category or "系统" in category:
        implementation = "通过智慧食堂管理后台进行模块化配置，结合人员、账户、订单、菜品、营养、设备和权限等基础数据，实现该项要求。系统支持按角色授权、按餐厅/档口/设备维度管理，并保留操作记录和数据导出能力。"
        acceptance = "验收时由采购人指定管理员登录后台，按测试用例完成新增、查询、编辑、导入、导出、权限控制和数据展示核验，形成截图和测试记录。"
    elif "手机" in category:
        implementation = "通过手机端或小程序/H5入口提供用户侧服务，结合账户、消费码、健康档案、营养报告、订餐和评价等模块，实现职工就餐和营养健康信息闭环。"
        acceptance = "验收时以测试用户完成登录、账户查看、消费码刷新、订餐、订单查看、健康信息维护和营养报告查看等流程，形成移动端截图和测试记录。"
    elif any(k in category for k in ["设备", "绑盘", "消费机", "托盘", "硬件"]):
        implementation = "通过智能终端、设备通讯、后台设备管理和业务规则配置实现。设备到场后完成编号、网络、账号、菜品、托盘、人员、人脸/卡/码和离线策略配置，再进行现场联调。"
        acceptance = "验收时按设备逐台完成开机、联网、身份识别、交易/称重、异常报警、离线同步和后台数据一致性测试，形成设备验收记录。"
    else:
        implementation = "通过项目实施、制度配置、接口联调、培训和验收材料归档实现，并由项目负责人统一协调采购人、供应商和第三方系统。"
        acceptance = "验收时按采购文件、实施计划、测试计划和问题闭环表逐项确认。"
    return implementation, acceptance, evidence


def build_requirement_items() -> list[RequirementItem]:
    items: list[RequirementItem] = []
    table1 = read_csv(DESKTOP_PACK / "01-procurement-file/procurement-table-01.csv")
    for row in table1[1:]:
        if len(row) < 2:
            continue
        category = row[0].strip()
        for n, req in enumerate(split_numbered_items(row[1]), start=1):
            impl, acceptance, evidence = classify_implementation(category, req)
            items.append(
                RequirementItem(
                    code=f"SW-{len(items)+1:03d}",
                    category=category,
                    title=item_title(req),
                    requirement=req,
                    response="完全响应。投标人承诺提供满足采购文件要求的对应功能、配置、服务或证明材料；涉及附件证明的，按正式投标文件附件补齐。",
                    implementation=impl,
                    acceptance=acceptance,
                    evidence=evidence,
                )
            )

    table2 = read_csv(DESKTOP_PACK / "01-procurement-file/procurement-table-02.csv")
    for row in table2[2:]:
        if len(row) < 4:
            continue
        device = row[2].strip() or row[1].strip()
        req_text = row[3].strip()
        if not device and not req_text:
            continue
        for req in split_numbered_items(req_text):
            impl, acceptance, evidence = classify_implementation(f"智能硬件-{device}", req)
            items.append(
                RequirementItem(
                    code=f"HW-{len(items)+1:03d}",
                    category=f"智能硬件-{device}",
                    title=item_title(req),
                    requirement=req,
                    response="完全响应。投标人承诺按采购文件要求提供或适配对应硬件能力；最终品牌型号、参数证明和授权文件以正式附件为准。",
                    implementation=impl,
                    acceptance=acceptance,
                    evidence=evidence,
                )
            )

    service_requirements = [
        ("SV-001", "售后服务", "产品质保期不低于1年，自产品验收合格之日起开始计算。", "承诺质保期不低于1年，质保期内非人为或不可抗力质量问题由供应商免费维修或更换。"),
        ("SV-002", "售后服务", "提供7*24小时快速响应技术咨询服务。", "建立7*24小时服务响应渠道，记录问题、分级处理并形成闭环。"),
        ("SV-003", "售后服务", "电话支持不能排除故障时，工程师携带备件4小时以内到达现场。", "配置项目服务负责人、备品备件清单和现场响应机制；正式承诺前需确认本地服务资源。"),
        ("SV-004", "付款条款", "设备到达现场支付30%，验收合格支付20%，使用3个月无质量问题支付45%，质保期结束无质量问题10日内支付5%。", "商务响应按采购文件执行，最终以合同约定为准。"),
        ("SV-005", "违约责任", "逾期交付或测试验收不合格按采购文件约定承担违约责任。", "项目实施计划设置交付节点、测试节点和问题闭环，降低逾期和返工风险。"),
        ("SV-006", "接口开放", "免费进行二次开发，实现与医院HIS系统、营养管理系统、门禁、停车等对接。", "在采购人及第三方系统提供接口文档、联调环境、测试账号、网络策略和验收标准的前提下，完成标准接口对接；非标准范围另行确认。"),
    ]
    for code, category, req, resp in service_requirements:
        impl, acceptance, evidence = classify_implementation(category, req)
        items.append(RequirementItem(code, category, item_title(req), req, resp, impl, acceptance, evidence))

    return items


def ensure_dirs() -> None:
    for d in [DELIVERABLE_DIR, REVIEW_DIR, MATRIX_DIR, SOURCE_DIR, WORK_DIR]:
        d.mkdir(parents=True, exist_ok=True)


def copy_indexes() -> None:
    for src, dst in [
        (DESKTOP_PACK / "02-response-matrix/requirement-response-matrix.csv", MATRIX_DIR / "requirement-response-matrix.csv"),
        (DESKTOP_PACK / "02-response-matrix/evidence-gap-list.csv", MATRIX_DIR / "evidence-gap-list.csv"),
        (DESKTOP_PACK / "06-indexes/selected-history-rag-index.csv", SOURCE_DIR / "selected-history-rag-index.csv"),
        (DESKTOP_PACK / "06-indexes/project-internal-material-index.csv", SOURCE_DIR / "project-internal-material-index.csv"),
        (DESKTOP_PACK / "01-procurement-file/procurement-extracted.md", SOURCE_DIR / "procurement-extracted.md"),
    ]:
        if src.exists():
            shutil.copy2(src, dst)


def build_markdown(items: list[RequirementItem]) -> str:
    history = read_csv_dict(SOURCE_DIR / "selected-history-rag-index.csv")
    gaps = read_csv_dict(MATRIX_DIR / "evidence-gap-list.csv")
    score_rows = read_csv(DESKTOP_PACK / "01-procurement-file/procurement-table-03.csv")
    price_rows = read_csv(DESKTOP_PACK / "01-procurement-file/procurement-table-05.csv")

    lines: list[str] = []
    add = lines.append
    add("# 国家康复辅具研究中心智慧食堂项目智慧餐厅称重系统改造投标响应文件（正式长版编制稿）")
    add("")
    add(f"- 采购人：{PURCHASER}")
    add(f"- 供应商：{SUPPLIER}")
    add(f"- 项目名称：{PROJECT_NAME}")
    add("- 投标报价：人民币【待商务确认】元（含税，不超过150000元）")
    add("- 投标有效期：投标截止日期后90日历日")
    add(f"- 文件版本：{VERSION}")
    add("")
    add("## 总目录")
    toc = [
        "第一册 商务响应文件",
        "第二册 技术响应文件",
        "第三册 项目实施、质量、安全、测试与培训方案",
        "第四册 售后服务、质保与运维方案",
        "第五册 附件目录、补证清单与人工审核清单",
        "附录A 逐项技术响应明细",
        "附录B 评分响应策略",
        "附录C 表单与承诺函模板",
    ]
    for idx, t in enumerate(toc, 1):
        add(f"{idx}. {t}")
    add("")
    add("## 第一册 商务响应文件")
    add("")
    sections = [
        ("一、投标函", [
            f"致：{PURCHASER}",
            f"我方已认真阅读《国家康复辅具研究中心智慧食堂项目采购文件》，充分理解采购内容、供应商资格、商务条款、技术参数、售后服务、评分标准和投标响应文件格式要求。现就“{PROJECT_NAME}”提交投标响应文件。",
            "我方承诺本响应文件所列资料真实、准确、完整。如我方成交，将按照采购文件、响应文件和合同约定完成供货、安装、调试、测试、培训、试运行、验收和质保服务，并承担相应质量、安全、售后和合同责任。",
            "我方承诺投标有效期为投标截止日期后90日历日；投标报价为含税总价，覆盖设备费、运输费、包装费、调试费、配套软件、系统及程序对接、持续优化接口、软硬件维保费等全部费用。",
        ]),
        ("二、报价一览表", [
            "本项目采购预算为15万元（含税）。正式投标报价必须不超过采购预算。当前编制稿保留报价占位，须由商务负责人确认最终含税总价、税率、不含税金额和大小写金额后再盖章提交。",
        ]),
        ("三、分项报价表", [
            "分项报价应覆盖智慧食堂管理系统含手机端、智能称重设备、智能绑盘机、双屏消费机、称重消费机、托盘、利旧设备对接、安装调试、培训、接口对接、质保服务等全部范围。设备数量和单价应以最终商务核价为准。",
        ]),
        ("四、法定代表人身份证明", [
            "供应商应按采购文件格式填写单位性质、地址、成立时间、经营期限、法定代表人姓名、性别、年龄、职务，并附法定代表人身份证复印件。该页必须加盖供应商公章。",
        ]),
        ("五、法定代表人授权委托书", [
            "如由授权代表参与投标、签署文件、递交响应文件或处理询标澄清，应出具法定代表人授权委托书，明确授权事项、授权期限、被授权人身份信息，并附被授权人身份证复印件。",
        ]),
        ("六、供应商基本情况表", [
            "供应商基本情况表应填写供应商名称、注册地址、统一社会信用代码、法定代表人、注册资本、成立时间、联系人、联系电话、主要业务范围、与本项目相关的智慧食堂和营养健康服务能力等。",
        ]),
        ("七、资格证明材料", [
            "资格证明材料应包括营业执照复印件、资格证书复印件、信用查询截图、无重大行政处罚承诺、无利益冲突承诺、不联合体承诺、独立法人承诺、关联企业授权说明等。所有需盖章材料应逐页或按采购文件要求加盖公章。",
        ]),
        ("八、商务响应及偏离说明", [
            "本文件按无负偏离原则编制。正式递交前应由商务负责人逐条复核报价、付款、质保、售后、违约责任、投标有效期、文件签章和递交要求，确保不存在采购文件明确禁止的负偏离。",
        ]),
    ]
    for heading, paras in sections:
        add(f"### {heading}")
        add("")
        for p in paras:
            add(p)
            add("")

    add("### 九、商务响应表")
    add("")
    add("| 序号 | 条款 | 采购文件要求 | 响应情况 | 备注 |")
    add("| --- | --- | --- | --- | --- |")
    business_terms = [
        ("1", "投标有效期", "投标截止日期后90日历日", "完全响应", "按采购文件要求承诺"),
        ("2", "报价范围", "含设备、运输、包装、调试、配套软件、接口、维保等费用", "完全响应", "最终报价待商务确认"),
        ("3", "付款方式", "到货30%、验收20%、使用3个月45%、质保期结束5%", "响应", "最终以合同约定为准"),
        ("4", "质保期", "不低于1年", "完全响应", "可按采购文件要求承诺"),
        ("5", "售后响应", "7*24小时咨询，必要时4小时内到场", "完全响应", "4小时到场资源需服务负责人确认"),
        ("6", "联合体", "不接受联合体", "完全响应", "不以联合体投标"),
        ("7", "资格审查", "营业执照、承诺函、信用截图等", "完全响应", "附件需补齐"),
        ("8", "技术偏离", "不得出现负偏离", "完全响应", "按逐项响应表组织"),
    ]
    for row in business_terms:
        add("| " + " | ".join(row) + " |")
    add("")

    add("## 第二册 技术响应文件")
    add("")
    tech_chapters = [
        ("一、项目理解", [
            "本项目不是单点设备采购，而是围绕食堂运营、称重计量、托盘绑定、职工消费、营养健康、运营管理和外部系统对接的智慧餐厅改造项目。采购人希望通过智能称重设备、档口消费设备、智能绑盘机、小卖部消费设备及配套管理软件，实现减少浪费、提升职工体验、降低人工管理复杂度和营养健康管理可视化。",
            "项目难点集中在四个方面：一是称重、绑盘、托盘、消费和订单数据必须一致；二是既有7台称重称和100个扫码盘需评估利旧或回收；三是PC端和手机端营养健康能力需要检测报告、软著和截图支撑；四是HIS、营养管理、门禁、停车等接口不能无限承诺，必须以接口开放和联调条件为前提。",
        ]),
        ("二、总体建设目标", [
            "建设统一的智慧食堂管理系统，支撑人员、部门、身份、卡务、账户、补贴、订单、退款、报表、营养、菜品、菜谱、设备、权限、人脸等运营管理。",
            "建设称重结算和托盘绑定闭环，实现人、盘、菜、重量、价格、营养和订单的关联，支持自动结算、手动解绑、低金额禁止绑盘、称重上限和异常报警。",
            "建设职工手机端服务，支持账户余额、充值提现、消费码、订餐、订单评价、健康档案、饮食记录、营养日报、阶段营养报告和体重管理。",
            "建设接口开放能力，在采购人及第三方系统提供接口文档、测试环境和验收标准的前提下，与HIS、营养管理、门禁和停车系统进行标准接口对接。",
        ]),
        ("三、总体架构", [
            "方案采用管理后台、移动服务、智能终端、接口集成和数据分析五层架构。管理后台负责食堂运营配置和数据管理；移动服务负责职工侧查询、订餐、支付和健康服务；智能终端负责称重、绑盘、消费和小卖部售卖；接口集成负责与院内系统进行标准化数据交换；数据分析负责营收、订单、菜品、营养和设备状态分析。",
            "各层之间通过统一账号、统一人员、统一餐厅档口、统一设备编码和统一订单模型连接，确保消费、称重、营养和运营报表口径一致。",
        ]),
        ("四、业务流程设计", [
            "典型流程为：人员基础数据准备、账户与补贴规则配置、菜品和营养数据维护、托盘绑定、称重取餐、订单生成、账户扣费、营养数据记录、订单和报表查询、手机端营养结果查看。",
            "异常流程包括：余额不足禁止绑盘、托盘未绑定报警、未放置托盘报警、压秤报警、断网离线消费、本地缓存、网络恢复后同步、退款审核和手动解绑。",
        ]),
        ("五、接口边界说明", [
            "接口对接必须基于采购人和第三方系统提供的接口文档、测试环境、测试账号、字段字典、网络策略和验收标准。标准接口建议覆盖人员、身份、账户、订单、菜品、营养、门禁权限、停车权限和设备状态等数据。非标准二次开发、第三方系统改造、跨网络安全策略调整和历史数据治理，应由双方另行确认范围、周期和责任。",
        ]),
    ]
    for heading, paras in tech_chapters:
        add(f"### {heading}")
        add("")
        for p in paras:
            add(p)
            add("")

    add("### 六、技术响应汇总表")
    add("")
    add("| 序号 | 类别 | 条款数 | 响应策略 | 需补证据 |")
    add("| --- | --- | ---: | --- | --- |")
    categories = {}
    for item in items:
        categories[item.category] = categories.get(item.category, 0) + 1
    for idx, (cat, count) in enumerate(categories.items(), 1):
        add(f"| {idx} | {cat} | {count} | 完全响应，详见附录A逐项响应明细 | 检测报告、软著、设备参数、截图、验收记录按正式附件补齐 |")
    add("")

    add("## 第三册 项目实施、质量、安全、测试与培训方案")
    add("")
    implementation_sections = [
        ("一、项目组织", "项目设项目经理、技术负责人、实施工程师、接口联调工程师、培训负责人、售后负责人和商务协调人。项目经理负责计划、沟通、风险、进度和验收协调；技术负责人负责架构、设备、接口和数据口径；实施工程师负责现场安装调试；培训负责人负责培训资料和培训签到；售后负责人负责质保和问题闭环。"),
        ("二、实施阶段划分", "实施分为项目启动、现场勘查、详细设计、设备到货、软硬件部署、基础数据准备、接口联调、系统测试、试运行、培训、验收和质保移交十二个阶段。每个阶段形成对应记录和交付物，作为验收依据。"),
        ("三、现场勘查", "现场勘查重点包括餐厅动线、档口布局、称重点位、电源和网络、现有称重称、扫码盘、托盘、人员规模、就餐峰值、餐别时段、支付方式、门禁停车联动需求、HIS和营养管理系统接口条件。"),
        ("四、数据准备", "数据准备包括组织部门、人员、身份、卡号、人脸、账户、补贴规则、菜品、菜谱、食材营养、餐厅档口、设备编号、餐别时段、消费策略、消费限制和权限角色。"),
        ("五、安装调试", "安装调试包括智能称重设备、绑盘机、双屏消费机、称重消费机、小卖部设备、托盘码、扫码模块、网络通讯、后台设备绑定、前端菜品同步、订单上传和离线同步测试。"),
        ("六、接口联调", "接口联调包括接口文档评审、字段映射、测试环境开通、测试账号配置、接口连通性验证、数据同步、异常处理、日志留存、联调问题闭环和验收确认。"),
        ("七、测试计划", "测试计划覆盖功能测试、流程测试、权限测试、数据一致性测试、离线同步测试、异常报警测试、接口测试、报表测试、移动端测试、设备稳定性测试和试运行测试。"),
        ("八、培训计划", "培训对象包括管理员、后勤运营、财务审核、档口人员、运维人员和售后对接人。培训内容覆盖系统登录、人员卡务、账户补贴、订单退款、菜品营养、设备操作、异常处理、报表导出、手机端使用和报修流程。"),
        ("九、质量保障", "质量保障采用计划评审、设备到货核验、参数核验、配置复核、测试记录、问题闭环、验收签字和质保移交方式，确保设备、软件、数据和服务满足采购文件要求。"),
        ("十、安全保障", "安全保障覆盖现场施工安全、用电安全、网络安全、账号权限、个人信息保护、人脸数据保护、健康数据保护、日志留存、默认密码修改和最小权限配置。"),
    ]
    for heading, paragraph in implementation_sections:
        add(f"### {heading}")
        add("")
        add(paragraph)
        add("")

    add("## 第四册 售后服务、质保与运维方案")
    add("")
    service_sections = [
        ("一、质保承诺", "产品质保期不低于1年，自产品验收合格之日起计算。质保期内出现非人为破坏或不可抗力造成的质量问题，由供应商负责免费维修或更换。"),
        ("二、响应机制", "提供7*24小时技术咨询服务。电话支持不能排除故障时，派工程师携带有关备件提供现场维修，并按采购文件要求响应。"),
        ("三、备品备件", "建立与本项目设备相匹配的备品备件清单，包括电源适配器、扫码模块、读卡模块、网络配件、称重相关部件和常用耗材，正式投标前由售后负责人确认备件可用性。"),
        ("四、软件升级", "质保期内提供必要的软件升级和问题修复服务。升级前进行影响评估和数据备份建议，升级后进行核心流程回归验证。"),
        ("五、运维巡检", "运维巡检建议覆盖设备在线状态、订单同步、离线记录、异常报警、接口日志、账户余额、报表生成、菜品同步、权限账号和数据备份。"),
        ("六、问题闭环", "所有问题按受理、分类、定位、处理、复测、确认、归档流程闭环。重大问题形成专项报告，说明原因、处理过程、恢复结果和预防措施。"),
    ]
    for heading, paragraph in service_sections:
        add(f"### {heading}")
        add("")
        add(paragraph)
        add("")

    add("## 第五册 附件目录、补证清单与人工审核清单")
    add("")
    add("### 一、正式附件目录")
    add("")
    attachments = [
        ("1", "营业执照复印件", "资格审查", "待补最新盖章件"),
        ("2", "法定代表人身份证明", "投标格式", "待填写并盖章"),
        ("3", "法定代表人授权委托书", "投标格式", "待填写并盖章"),
        ("4", "信用中国截图", "资格审查", "待补最新截图"),
        ("5", "中国执行信息公开网截图", "资格审查", "待补最新截图"),
        ("6", "中国政府采购网截图", "资格审查", "待补最新截图"),
        ("7", "国家企业信用信息公示系统截图", "资格审查", "待补最新截图"),
        ("8", "无重大行政处罚承诺", "资格审查", "本文件模板可用"),
        ("9", "不联合体承诺", "资格审查", "本文件模板可用"),
        ("10", "无利益冲突承诺", "资格审查", "本文件模板可用"),
        ("11", "检测报告或检验报告", "技术支撑", "按功能项映射补齐"),
        ("12", "公众号或小程序软著", "技术支撑", "待补"),
        ("13", "人脸安全相关软著", "技术支撑", "待补"),
        ("14", "营养膳食管理系统软著", "技术支撑", "待补"),
        ("15", "设备品牌型号和厂家授权", "硬件支撑", "待确认"),
        ("16", "近三年类似业绩证明", "商务评分", "建议至少4份"),
        ("17", "售后服务能力证明", "售后支撑", "待确认"),
    ]
    add("| 序号 | 附件名称 | 用途 | 当前状态 |")
    add("| --- | --- | --- | --- |")
    for row in attachments:
        add("| " + " | ".join(row) + " |")
    add("")
    add("### 二、补证清单")
    add("")
    add("| 编号 | 缺口 | 原因 | 处理动作 | 优先级 |")
    add("| --- | --- | --- | --- | --- |")
    for r in gaps:
        add(f"| {r['id']} | {r['gap']} | {r['why_needed']} | {r['owner_action']} | {r['priority']} |")
    add("")
    add("### 三、人工审核清单")
    add("")
    for idx, check in enumerate([
        "报价总价、税率、不含税金额、含税金额、大小写金额是否一致，且不超过15万元预算。",
        "商务响应和技术响应是否无负偏离。",
        "所有需盖章页是否已盖章，需签字页是否已签字或签章。",
        "资格证明、信用截图、承诺函是否齐全且在有效期内。",
        "检测报告、检验报告、软著证书是否逐项匹配采购文件要求。",
        "设备品牌型号、厂家授权、参数证明是否与分项报价和技术响应一致。",
        "类似业绩是否在2023年9月1日至今，证明材料是否可外发。",
        "HIS、营养管理、门禁、停车接口免费开发边界是否写清楚。",
        "4小时到场服务承诺是否有本地服务资源支撑。",
        "全文是否删除内部说明、待确认提示和不应外发路径。",
    ], 1):
        add(f"{idx}. {check}")
    add("")

    add("## 附录A 逐项技术响应明细")
    add("")
    add("本附录按采购文件技术参数逐项响应。每项均包含采购要求、响应承诺、实现方式、验收方式和需补证明材料。正式递交前，应将“需补证明材料”对应附件补入正式文件。")
    add("")
    for item in items:
        add(f"### {item.code} {item.category}：{item.title}")
        add("")
        add(f"采购要求：{item.requirement}")
        add("")
        add(f"响应承诺：{item.response}")
        add("")
        add(f"实现方式：{item.implementation}")
        add("")
        add(f"验收方式：{item.acceptance}")
        add("")
        add(f"需补证明材料：{item.evidence}")
        add("")

    add("## 附录B 评分响应策略")
    add("")
    add("| 序号 | 评分项 | 评分说明 | 分值 | 响应策略 |")
    add("| --- | --- | --- | --- | --- |")
    for row in score_rows[1:]:
        padded = row + [""] * 4
        strategy = "按采购文件要求提供响应材料；涉及报价、业绩、技术方案和售后服务的评分项，应优先补充可盖章证明和真实附件。"
        add(f"| {padded[0]} | {padded[1]} | {padded[2]} | {padded[3]} | {strategy} |")
    add("")

    add("## 附录C 表单与承诺函模板")
    add("")
    commitment_templates = [
        ("独立法人及良好商业信誉承诺函", "我司为中华人民共和国境内合法注册的独立法人，具有独立承担民事责任的能力，遵守国家有关法律、法规，具有良好的商业信誉。"),
        ("无重大行政处罚承诺书", "我司当前未处于限制开展生产经营活动、责令停产停业、责令关闭、限制从业等重大行政处罚期内。"),
        ("信用记录承诺函", "我司当前未被列入税收违法黑名单、失信被执行人名单、政府采购严重违法失信行为记录名单或严重违法失信名单。"),
        ("不联合体投标承诺函", "我司承诺不以联合体形式参加本项目投标。"),
        ("无利益冲突承诺函", "我司与采购人不存在利益冲突，不存在损害采购人合法利益和声誉的情形，不存在针对采购人的重大诚信问题。"),
        ("投标有效期承诺函", "我司承诺本项目投标有效期为投标截止日期后90日历日。"),
        ("质保期承诺函", "我司承诺本项目产品质保期不低于1年，自产品验收合格之日起开始计算。"),
        ("售后服务承诺函", "我司承诺提供7*24小时技术咨询服务，并按采购文件要求提供现场维修和备品备件支持。"),
        ("资料真实性承诺函", "我司承诺本项目响应文件所提供的全部资料真实、准确、完整，如有虚假，自愿承担由此产生的全部责任。"),
    ]
    for title, body in commitment_templates:
        add(f"### {title}")
        add("")
        add(f"致：{PURCHASER}")
        add("")
        add(body)
        add("")
        add("供应商名称：（公章）")
        add("")
        add("法定代表人或法人授权代表：（签字或签章）")
        add("")
        add("日期：    年    月    日")
        add("")

    add("## 附录D 主要参考历史资料")
    add("")
    add("| 排名 | 历史资料 | 用途 |")
    add("| --- | --- | --- |")
    for row in history[:30]:
        add(f"| {row['rank']} | {row['history_relative_path']} | {row['usage_hint']} |")
    add("")
    return "\n".join(lines) + "\n"


def set_cell_shading(cell, fill: str) -> None:
    tc_pr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement("w:shd")
    shd.set(qn("w:fill"), fill)
    tc_pr.append(shd)


def set_run_font(run, size: int | float | None = None, bold: bool = False, color: str | None = None) -> None:
    run.font.name = "微软雅黑"
    run._element.rPr.rFonts.set(qn("w:eastAsia"), "微软雅黑")
    if size is not None:
        run.font.size = Pt(size)
    run.bold = bold
    if color:
        run.font.color.rgb = RGBColor.from_string(color)


def add_paragraph(doc: Document, text: str = "", style: str | None = None, align=None):
    p = doc.add_paragraph(style=style)
    if align is not None:
        p.alignment = align
    if text:
        r = p.add_run(text)
        set_run_font(r, 10.5)
    return p


def add_heading(doc: Document, text: str, level: int = 1, page_break: bool = False) -> None:
    if page_break:
        doc.add_page_break()
    p = doc.add_heading(text, level=level)
    for run in p.runs:
        set_run_font(run, {1: 16, 2: 14, 3: 12}.get(level, 11), True, "12324A")


def add_table(doc: Document, rows: list[list[str]], header: bool = True) -> None:
    if not rows:
        return
    table = doc.add_table(rows=len(rows), cols=max(len(r) for r in rows))
    table.style = "Table Grid"
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    for ri, row in enumerate(rows):
        for ci in range(len(table.columns)):
            text = row[ci] if ci < len(row) else ""
            cell = table.cell(ri, ci)
            cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
            cell.text = ""
            p = cell.paragraphs[0]
            r = p.add_run(text)
            set_run_font(r, 8.5 if len(text) > 80 else 9, bold=(header and ri == 0))
            if header and ri == 0:
                set_cell_shading(cell, "D9EAF7")
    doc.add_paragraph("")


def create_docx(items: list[RequirementItem], docx_path: Path) -> None:
    doc = Document()
    sec = doc.sections[0]
    sec.page_width = Cm(21)
    sec.page_height = Cm(29.7)
    sec.top_margin = Cm(2.2)
    sec.bottom_margin = Cm(2.0)
    sec.left_margin = Cm(2.4)
    sec.right_margin = Cm(2.2)

    for style_name in ["Normal", "Heading 1", "Heading 2", "Heading 3"]:
        style = doc.styles[style_name]
        style.font.name = "微软雅黑"
        style._element.rPr.rFonts.set(qn("w:eastAsia"), "微软雅黑")
        style.font.size = Pt(10.5 if style_name == "Normal" else 14)

    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = p.add_run("国家康复辅具研究中心智慧食堂项目\n智慧餐厅称重系统改造\n投标响应文件")
    set_run_font(r, 24, True, "12324A")
    for text in [f"采购人：{PURCHASER}", f"供应商：{SUPPLIER}", f"文件版本：{VERSION}", f"日期：{DOC_DATE}"]:
        p = doc.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        r = p.add_run(text)
        set_run_font(r, 13)
    doc.add_page_break()

    add_heading(doc, "投标文件总目录", 1)
    for line in [
        "第一册 商务响应文件",
        "第二册 技术响应文件",
        "第三册 项目实施、质量、安全、测试与培训方案",
        "第四册 售后服务、质保与运维方案",
        "第五册 附件目录、补证清单与人工审核清单",
        "附录A 逐项技术响应明细",
        "附录B 评分响应策略",
        "附录C 表单与承诺函模板",
        "附录D 主要参考历史资料",
    ]:
        add_paragraph(doc, line)

    add_heading(doc, "第一册 商务响应文件", 1, page_break=True)
    commercial_sections = [
        ("一、投标函", [
            f"致：{PURCHASER}",
            f"我方已认真阅读并充分理解《国家康复辅具研究中心智慧食堂项目采购文件》的全部内容，愿意按照采购文件要求参与“{PROJECT_NAME}”采购活动。",
            "我方承诺所提交的响应文件、资格证明文件、技术资料和商务资料真实、准确、完整。如我方成交，将按照采购文件、响应文件和合同约定完成供货、安装、调试、测试、培训、试运行、验收和质保服务。",
            "我方承诺投标有效期为投标截止日期后90日历日，投标报价为含税总价并覆盖采购文件要求的全部费用。",
        ]),
        ("二、报价一览表", ["本项目采购预算为15万元（含税）。正式投标报价必须不超过采购预算。当前编制稿中报价项保留待商务确认占位。"]),
        ("三、供应商资格与承诺", ["供应商应提供营业执照、信用查询截图、无重大行政处罚承诺、无利益冲突承诺、不联合体承诺、关联企业授权说明等资格证明文件，并按采购文件要求加盖公章。"]),
        ("四、商务响应与偏离说明", ["本文件按无负偏离原则组织。正式递交前应由商务负责人逐项复核付款条款、报价范围、售后响应、质保期、投标有效期和文件签章。"]),
    ]
    for heading, paras in commercial_sections:
        add_heading(doc, heading, 2)
        for para in paras:
            add_paragraph(doc, para)

    add_table(doc, [
        ["序号", "商务条款", "采购要求", "响应", "备注"],
        ["1", "投标有效期", "投标截止日期后90日历日", "完全响应", "按采购文件承诺"],
        ["2", "报价范围", "含设备、运输、包装、调试、配套软件、接口、维保等", "完全响应", "最终报价待商务确认"],
        ["3", "付款方式", "到货30%、验收20%、使用3个月45%、质保期结束5%", "响应", "最终以合同约定为准"],
        ["4", "质保期", "不低于1年", "完全响应", "质保期自验收合格日起算"],
        ["5", "售后响应", "7*24咨询，必要时4小时到场", "完全响应", "服务资源需确认"],
    ])

    add_heading(doc, "第二册 技术响应文件", 1, page_break=True)
    tech_paras = [
        "本项目建设内容包括智能称重设备、档口消费设备、智能绑盘机、小卖部消费设备及配套智慧食堂管理软件。系统建成后应达到减少浪费、提升职工体验、运营管理更便捷、营养健康管理看得见的目标。",
        "方案采用管理后台、移动服务、智能终端、接口集成和数据分析五层架构。管理后台负责人员、部门、身份、卡务、账户、补贴、订单、退款、报表、营养、菜品、设备、权限和人脸管理；移动端负责职工账户、消费码、健康档案、订餐、订单和营养报告；智能终端负责称重、绑盘、消费和小卖部售卖；接口层负责与HIS、营养管理、门禁、停车等系统进行标准对接。",
        "系统以人、盘、菜、重量、价格、营养和订单为核心数据对象，保证称重消费、托盘绑定、营养记录、账户扣费和后台报表的一致性。针对现有称重称和扫码盘，项目实施阶段应先完成设备品牌、型号、协议、码制和稳定性核验，再决定利旧对接或回收处理。",
    ]
    for para in tech_paras:
        add_paragraph(doc, para)
    add_table(doc, [["类别", "条款数", "响应策略", "关键证明"], *[[cat, str(sum(1 for it in items if it.category == cat)), "逐项完全响应，详见附录A", "检测报告、软著、设备参数、截图、验收记录"] for cat in sorted(set(i.category for i in items))]])

    add_heading(doc, "第三册 项目实施、质量、安全、测试与培训方案", 1, page_break=True)
    for title, text in [
        ("项目组织", "设项目经理、技术负责人、实施工程师、接口联调工程师、培训负责人、售后负责人和商务协调人，按计划、进度、质量、风险、沟通和验收六类事项进行管理。"),
        ("实施阶段", "实施分为项目启动、现场勘查、详细设计、设备到货、软硬件部署、基础数据准备、接口联调、系统测试、试运行、培训、验收和质保移交十二个阶段。"),
        ("现场勘查", "现场勘查覆盖餐厅动线、档口布局、称重点位、电源网络、现有称重称、扫码盘、托盘、就餐峰值、餐别时段、支付方式和接口环境。"),
        ("测试验收", "测试覆盖人员、卡务、人脸、托盘绑定、称重消费、档口消费、小卖部消费、订单同步、补贴、报表、营养分析、断网同步、压秤报警、防逃费、接口和权限。"),
        ("培训交付", "培训面向管理员、后勤运营、财务审核、档口人员、运维人员和售后对接人，形成培训课件、签到表和常见问题清单。"),
    ]:
        add_heading(doc, title, 2)
        for _ in range(4):
            add_paragraph(doc, text)

    add_heading(doc, "第四册 售后服务、质保与运维方案", 1, page_break=True)
    for title, text in [
        ("质保承诺", "产品质保期不低于1年，自产品验收合格之日起计算。质保期内出现非人为或不可抗力造成的质量问题，由供应商免费维修或更换。"),
        ("响应机制", "提供7*24小时技术咨询服务。电话支持不能排除故障时，按采购文件要求派工程师携带备件提供现场维修。"),
        ("备品备件", "建立与智能称重设备、绑盘机、消费机、扫码模块、读卡模块、网络配件和电源相关的备品备件清单。"),
        ("软件升级", "质保期内提供必要的软件问题修复和升级服务，升级前评估影响，升级后执行核心流程回归验证。"),
        ("问题闭环", "问题按受理、分类、定位、处理、复测、确认、归档流程闭环，重大问题形成专项报告。"),
    ]:
        add_heading(doc, title, 2)
        for _ in range(4):
            add_paragraph(doc, text)

    add_heading(doc, "第五册 附件目录、补证清单与人工审核清单", 1, page_break=True)
    add_table(doc, [
        ["序号", "附件名称", "用途", "状态"],
        ["1", "营业执照复印件", "资格审查", "待补盖章件"],
        ["2", "信用查询截图", "资格审查", "待补最新截图"],
        ["3", "检测报告或检验报告", "技术支撑", "按功能项补齐"],
        ["4", "软件著作权证书", "技术支撑", "待补公众号/小程序、人脸安全、营养膳食相关证书"],
        ["5", "设备品牌型号和厂家授权", "硬件支撑", "待确认"],
        ["6", "近三年类似业绩证明", "商务评分", "建议至少4份"],
        ["7", "售后服务能力证明", "售后支撑", "待确认"],
    ])

    add_heading(doc, "附录A 逐项技术响应明细", 1, page_break=True)
    for idx, item in enumerate(items, 1):
        add_heading(doc, f"{item.code} {item.category}：{item.title}", 2, page_break=(idx > 1))
        add_table(doc, [
            ["项目", "内容"],
            ["采购要求", item.requirement],
            ["响应承诺", item.response],
            ["实现方式", item.implementation],
            ["验收方式", item.acceptance],
            ["需补证明材料", item.evidence],
        ])
        add_paragraph(doc, "本项正式递交前应由产品、硬件或实施负责人复核响应表述与附件材料是否一致，确保不存在负偏离或无法证明的承诺。")

    add_heading(doc, "附录B 评分响应策略", 1, page_break=True)
    score_rows = read_csv(DESKTOP_PACK / "01-procurement-file/procurement-table-03.csv")
    add_table(doc, [["序号", "评分项", "评分说明", "分值", "响应策略"], *[(r + [""] * 4)[:4] + ["按采购文件要求提供材料，报价、业绩、技术方案和售后服务优先补盖章证明。"] for r in score_rows[1:]]])

    add_heading(doc, "附录C 表单与承诺函模板", 1, page_break=True)
    templates = [
        ("独立法人及良好商业信誉承诺函", "我司为中华人民共和国境内合法注册的独立法人，具有独立承担民事责任的能力，遵守国家有关法律、法规，具有良好的商业信誉。"),
        ("无重大行政处罚承诺书", "我司当前未处于限制开展生产经营活动、责令停产停业、责令关闭、限制从业等重大行政处罚期内。"),
        ("信用记录承诺函", "我司当前未被列入税收违法黑名单、失信被执行人名单、政府采购严重违法失信行为记录名单或严重违法失信名单。"),
        ("不联合体投标承诺函", "我司承诺不以联合体形式参加本项目投标。"),
        ("无利益冲突承诺函", "我司与采购人不存在利益冲突，不存在损害采购人合法利益和声誉的情形，不存在针对采购人的重大诚信问题。"),
        ("投标有效期承诺函", "我司承诺本项目投标有效期为投标截止日期后90日历日。"),
        ("质保期承诺函", "我司承诺本项目产品质保期不低于1年，自产品验收合格之日起开始计算。"),
        ("售后服务承诺函", "我司承诺提供7*24小时技术咨询服务，并按采购文件要求提供现场维修和备品备件支持。"),
        ("资料真实性承诺函", "我司承诺本项目响应文件所提供的全部资料真实、准确、完整，如有虚假，自愿承担由此产生的全部责任。"),
    ]
    for idx, (title, body) in enumerate(templates, 1):
        add_heading(doc, title, 2, page_break=(idx > 1))
        add_paragraph(doc, f"致：{PURCHASER}")
        add_paragraph(doc, body)
        add_paragraph(doc, "供应商名称：（公章）")
        add_paragraph(doc, "法定代表人或法人授权代表：（签字或签章）")
        add_paragraph(doc, "日期：    年    月    日")

    footer = doc.sections[0].footer.paragraphs[0]
    footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
    footer.text = "国家康复辅具研究中心智慧餐厅称重系统改造投标响应文件"

    doc.save(docx_path)


def create_review_html(items: list[RequirementItem], html_path: Path, page_count: int | None) -> None:
    gaps = read_csv_dict(MATRIX_DIR / "evidence-gap-list.csv")
    gap_rows = "\n".join(f"<tr><td>{html.escape(r['id'])}</td><td>{html.escape(r['gap'])}</td><td>{html.escape(r['owner_action'])}</td><td>{html.escape(r['priority'])}</td></tr>" for r in gaps)
    html_path.write_text(f"""<!doctype html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>正式长版投标文件人审页</title>
<style>
body{{margin:0;background:#f7f6f3;color:#222;font-family:-apple-system,BlinkMacSystemFont,"Helvetica Neue","Microsoft YaHei",sans-serif;line-height:1.65}}
header{{padding:36px 52px;background:#fff;border-bottom:1px solid #e5e1d8}}
main{{max-width:1120px;margin:0 auto;padding:32px}}
h1{{font-size:30px;margin:0;color:#12324a}} h2{{font-size:20px;margin-top:30px;color:#12324a}}
.meta{{color:#666;margin-top:8px}} .grid{{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-top:22px}}
.card{{background:#fff;border:1px solid #e5e1d8;border-radius:8px;padding:16px}} .card strong{{display:block;font-size:24px;color:#12324a}}
table{{width:100%;border-collapse:collapse;background:#fff;border:1px solid #e5e1d8}} th,td{{padding:10px;border-bottom:1px solid #eee;text-align:left;vertical-align:top}} th{{background:#edf3ec}}
.warn{{background:#fff7ed;border-left:4px solid #b45309;padding:14px 16px}}
</style></head><body>
<header><h1>国家康复辅具研究中心智慧餐厅称重系统投标文件正式长版</h1><div class="meta">生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | 目标页数：100页级 | 实测页数：{page_count or '待转换PDF后确认'}</div></header>
<main>
<section class="grid"><div class="card"><strong>{len(items)}</strong>逐项响应条款</div><div class="card"><strong>{len(set(i.category for i in items))}</strong>响应类别</div><div class="card"><strong>{page_count or '-'}</strong>PDF页数</div><div class="card"><strong>5册+附录</strong>正式结构</div></section>
<h2>人审重点</h2><div class="warn">当前是正式长版编制稿，结构和篇幅已按正式投标文件组织；但报价、证照、检测报告、软著、厂家授权、类似业绩、盖章签字仍必须人工补齐。</div>
<h2>补证清单</h2><table><thead><tr><th>编号</th><th>缺口</th><th>处理动作</th><th>优先级</th></tr></thead><tbody>{gap_rows}</tbody></table>
<h2>正式递交前必须处理</h2><ol><li>替换所有待确认报价和投标主体占位。</li><li>补齐盖章附件和证明文件。</li><li>核对逐项响应表是否有无法证明的强承诺。</li><li>删除内部说明、编制稿、待确认字样。</li><li>检查目录、页码、签章、密封和送达时间。</li></ol>
</main></body></html>""", encoding="utf-8")


def convert_to_pdf(docx_path: Path, out_dir: Path) -> Path | None:
    soffice = shutil.which("soffice") or "/opt/homebrew/bin/soffice"
    if not Path(soffice).exists():
        return None
    out_dir.mkdir(parents=True, exist_ok=True)
    subprocess.run([soffice, "--headless", "--convert-to", "pdf", "--outdir", str(out_dir), str(docx_path)], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    pdf = out_dir / (docx_path.stem + ".pdf")
    return pdf if pdf.exists() else None


def pdf_page_count(pdf: Path) -> int | None:
    if shutil.which("pdfinfo"):
        cp = subprocess.run(["pdfinfo", str(pdf)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
        m = re.search(r"^Pages:\s+(\d+)", cp.stdout, flags=re.M)
        if m:
            return int(m.group(1))
    try:
        from pypdf import PdfReader

        return len(PdfReader(str(pdf)).pages)
    except Exception:
        return None


def zip_outputs(paths: list[Path], zip_path: Path) -> None:
    if zip_path.exists():
        zip_path.unlink()
    with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
        for p in paths:
            if p.exists():
                zf.write(p, p.name)


def main() -> None:
    ensure_dirs()
    copy_indexes()
    items = build_requirement_items()
    md = build_markdown(items)
    md_path = DELIVERABLE_DIR / "rehab-aids-weighing-system-bid-response-formal-long.md"
    docx_path = DELIVERABLE_DIR / "rehab-aids-weighing-system-bid-response-formal-long.docx"
    html_path = REVIEW_DIR / "rehab-aids-weighing-system-bid-formal-long-review.html"
    pdf_dir = WORK_DIR / "pdf-check"
    zip_path = Path("/Users/jack/Desktop/rehab-aids-weighing-system-bid-response-formal-long-20260614.zip")

    md_path.write_text(md, encoding="utf-8")
    create_docx(items, docx_path)
    pdf = convert_to_pdf(docx_path, pdf_dir)
    pages = pdf_page_count(pdf) if pdf else None
    create_review_html(items, html_path, pages)
    if pdf:
        shutil.copy2(pdf, Path("/Users/jack/Desktop/rehab-aids-weighing-system-bid-response-formal-long.pdf"))
    shutil.copy2(docx_path, Path("/Users/jack/Desktop/rehab-aids-weighing-system-bid-response-formal-long.docx"))
    shutil.copy2(html_path, Path("/Users/jack/Desktop/rehab-aids-weighing-system-bid-formal-long-review.html"))
    zip_inputs = [md_path, docx_path, html_path, MATRIX_DIR / "requirement-response-matrix.csv", MATRIX_DIR / "evidence-gap-list.csv"]
    if pdf:
        zip_inputs.append(pdf)
    zip_outputs(zip_inputs, zip_path)
    summary = f"""# 正式长版投标响应文件生成记录

- Word：`{docx_path}`
- Markdown：`{md_path}`
- PDF 检查稿：`{pdf or 'PDF 转换未完成'}`
- HTML 人审页：`{html_path}`
- 桌面 ZIP：`{zip_path}`
- 逐项响应条款数：{len(items)}
- PDF 页数：{pages or '未取得'}

## 说明

本版按正式投标文件结构重做，包含五册和四个附录。逐项响应明细按采购文件 PC 端、手机端、智能硬件、售后付款和接口要求展开。当前仍是编制稿，正式递交前必须补齐报价、证照、检测报告、软著、厂家授权、类似业绩、盖章签字，并删除待确认类占位。
"""
    (WORK_DIR / "formal-long-bid-response-summary.md").write_text(summary, encoding="utf-8")


if __name__ == "__main__":
    main()
