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

import csv
import hashlib
import json
import re
import shutil
import zipfile
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path

from docx import Document


REPO_ROOT = Path("/Users/jack/code/010-cpt/008-zhct/zhctprompt")
PROCUREMENT_DOCX = Path(
    "/Users/jack/Library/Containers/com.tencent.WeWorkMac/Data/Documents/Profiles/"
    "A74DF84611CC2ED79C4C206E40B5D80E/Caches/Files/2026-06/"
    "461a837fb39e14788c791c5d95ff2544/称重系统采购文件2026.4.28(6.11).docx"
)
TASK_DIR = REPO_ROOT / "work/2026-06-14-rehab-aids-weighing-bid-chatgpt-pack"
PACKAGE_ROOT = Path("/Users/jack/Desktop/rehab-aids-weighing-system-bid-chatgpt-pack-20260614")
HISTORY_MANIFEST = REPO_ROOT / "work/2026-06-13-presales-bidding-history-ingest/bidding-knowledge-manifest.csv"

KEY_TERMS = [
    "称重",
    "绑盘",
    "托盘",
    "智能餐线",
    "智慧食堂",
    "营养",
    "膳食",
    "消费机",
    "档口",
    "小卖部",
    "HIS",
    "门禁",
    "停车",
    "利旧",
    "断网",
    "压秤",
    "防逃费",
    "实施方案",
    "售后",
    "培训",
    "质保",
    "接口",
    "二次开发",
    "软件升级",
]

TERM_WEIGHTS = {
    "称重": 8,
    "绑盘": 8,
    "托盘": 6,
    "智能餐线": 8,
    "消费机": 5,
    "小卖部": 4,
    "利旧": 6,
    "断网": 5,
    "压秤": 6,
    "防逃费": 6,
    "HIS": 5,
    "门禁": 4,
    "停车": 4,
    "营养": 4,
    "膳食": 4,
}

INTERNAL_FILES = [
    "modules/presales-bidding/README.md",
    "modules/presales-bidding/templates/project-folder-template.md",
    "modules/presales-bidding/templates/requirement-response-matrix-template.csv",
    "modules/presales-bidding/templates/evidence-gate-checklist.md",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/README.md",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/standard-product-packages.md",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/control-parameter-library-v0.2.md",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/nutrition-health-control-parameter-library.md",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/software-function-parameters.csv",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/hardware-device-parameters.csv",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/product-qualification-parameters.csv",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/enterprise-qualification-parameters.csv",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/nutrition-health-control-parameter-library.csv",
    "standards-stack/product-strategy/smart-canteen/bid-parameter-library/differentiation-to-bid-control-map.csv",
    "product_kezuozhongqi-baokang-middle-school-smart-canteen/source-docs/extracted/bid-requirement-extract.md",
    "product_kezuozhongqi-baokang-middle-school-smart-canteen/tables/requirement-to-material-map.csv",
    "product_kezuozhongqi-baokang-middle-school-smart-canteen/deliverables/01-system-overall-architecture-design.md",
    "product_kezuozhongqi-baokang-middle-school-smart-canteen/deliverables/02-key-technology-route.md",
    "product_kezuozhongqi-baokang-middle-school-smart-canteen/deliverables/04-network-security-solution.md",
    "product_kezuozhongqi-baokang-middle-school-smart-canteen/quality-after-sales-training/summary.md",
    "work_suzhou_fuyao_smart_canteen/presales-intake.md",
    "work_suzhou_fuyao_smart_canteen/management-standards.md",
    "work_suzhou_fuyao_smart_canteen/source-docs/extracted/2026.5.13会议纪要_提取.md",
    "work_chongqing_huanwei/source-docs/extracted/260306智慧食堂方案.md",
    "work_chongqing_huanwei/coffee-machine-integration/client-and-deployment-confirmation.md",
    "work_chongqing_huanwei/coffee-machine-integration/integration-design.md",
    "work_chongqing_huanwei/coffee-machine-integration/responsibility-matrix.csv",
]


@dataclass
class SelectedHistory:
    score: int
    terms: list[str]
    row: dict[str, str]


def sha256_file(path: Path) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()


def clean_package_root() -> None:
    if PACKAGE_ROOT.exists():
        shutil.rmtree(PACKAGE_ROOT)
    for path in [
        "00-instructions",
        "01-procurement-file",
        "02-response-matrix",
        "03-rag-history-selected-markdown",
        "04-project-internal-materials",
        "05-raw-selected-originals",
        "06-indexes",
        "07-chatgpt-prompts",
        "08-output-skeleton",
    ]:
        (PACKAGE_ROOT / path).mkdir(parents=True, exist_ok=True)
    TASK_DIR.mkdir(parents=True, exist_ok=True)


def extract_procurement() -> dict[str, object]:
    doc = Document(PROCUREMENT_DOCX)
    lines: list[str] = []
    for para in doc.paragraphs:
        text = para.text.strip()
        if text:
            lines.append(text)

    table_paths = []
    for index, table in enumerate(doc.tables, start=1):
        csv_path = PACKAGE_ROOT / "01-procurement-file" / f"procurement-table-{index:02d}.csv"
        max_cols = max(len(row.cells) for row in table.rows)
        with csv_path.open("w", encoding="utf-8-sig", newline="") as f:
            writer = csv.writer(f)
            for row in table.rows:
                cells = [cell.text.strip().replace("\n", " / ") for cell in row.cells]
                cells.extend([""] * (max_cols - len(cells)))
                writer.writerow(cells)
        table_paths.append(csv_path)

    markdown = PACKAGE_ROOT / "01-procurement-file" / "procurement-extracted.md"
    parts = [
        "# 国家康复辅具研究中心智慧食堂项目采购文件抽取",
        "",
        f"- 原始文件：`{PROCUREMENT_DOCX}`",
        f"- SHA256：`{sha256_file(PROCUREMENT_DOCX)}`",
        f"- 抽取时间：{datetime.now().isoformat(timespec='seconds')}",
        "",
        "## 正文",
        "",
        "\n".join(lines),
        "",
        "## 表格索引",
        "",
    ]
    for p in table_paths:
        parts.append(f"- `{p.name}`")
    markdown.write_text("\n".join(parts) + "\n", encoding="utf-8")

    shutil.copy2(PROCUREMENT_DOCX, PACKAGE_ROOT / "01-procurement-file" / PROCUREMENT_DOCX.name)
    return {"paragraph_lines": len(lines), "tables": len(doc.tables), "markdown": str(markdown)}


def write_requirement_matrix() -> None:
    rows = [
        ["REQ-001", "项目名称", "智慧餐厅称重系统改造", "商务/总述", "必须响应", "在投标函、项目概述和技术方案首页一致表述", "采购文件第一章", "A", "无"],
        ["REQ-002", "预算", "15万元含税，报价不得超过预算", "报价", "必须响应", "报价表按整体项目报价，含税、施工、材料、运输、装卸、人工等全部费用", "采购文件第一章/第三章", "A", "最终价格需商务确认"],
        ["REQ-003", "递交时间", "2026年6月17日11:00前送达指定地点", "投标组织", "必须响应", "生成封装/盖章/送达检查清单", "采购公告", "A", "需人工确认打印盖章和送达安排"],
        ["REQ-004", "投标有效期", "投标截止日期后90日历日", "商务响应", "必须响应", "出具投标有效期承诺", "供应商须知", "A", "无"],
        ["REQ-005", "资格审查", "营业执照、承诺函、信用截图、不联合体、无利益冲突、关联企业授权", "资格文件", "必须响应", "按第五章格式准备资格证明文件", "第四章/第五章", "A", "证照和信用截图需最新人工提供"],
        ["REQ-006", "智慧食堂PC系统", "人员、卡务、部门、身份、充值补贴、订单、报表、营养统计、膳食监控、策略、排餐、菜品、设备、权限、人脸等52项", "技术响应", "逐项响应", "生成技术偏离/响应表，优先引用产品功能参数库和历史标书", "表格1", "A/B", "检测报告/检验报告项需补附件"],
        ["REQ-007", "手机端系统", "登录、双账户、二维码、健康信息、营养日报/周报、体重管理、线上订餐、订单评价、软著", "技术响应", "逐项响应", "形成移动端功能响应章节和资质附件清单", "表格1", "A/B", "公众号/小程序、人脸安全、营养膳食软著需人工补证"],
        ["REQ-008", "智能称重设备", "自助取餐计重、托盘/用餐者识别、营养显示、5g-30kg、精度、断网同步、压秤、防逃费等", "硬件响应", "逐项响应", "引用新奥智能餐线、自助称重结算台参数和项目硬件库", "表格2", "A/B", "最终品牌型号、截图和检测证据需确认"],
        ["REQ-009", "智能绑盘机", "人脸/卡/码绑定托盘，15.6寸屏，摄像头、读卡、扫码、配置维护", "硬件响应", "逐项响应", "引用新奥智能绑盘机参数和苏州福耀会议经验", "表格2", "A/B", "最终品牌型号、授权和截图需确认"],
        ["REQ-010", "双屏消费机/称重消费机", "刷卡、人脸、二维码、断网收银、报表、促销、电子秤参数", "硬件响应", "逐项响应", "引用消费机和称重消费设备能力材料", "表格2", "B", "最终设备型号和厂商参数需确认"],
        ["REQ-011", "托盘和利旧", "白色PP平托盘带码；现有7台称重称、100个扫码盘对接或原价收回", "实施/硬件", "必须响应", "写入利旧对接、现场勘查、兼容性测试和风险说明", "表格2", "A", "利旧设备型号、协议和收回价格需现场确认"],
        ["REQ-012", "质保售后", "质保不低于1年；7*24快速响应；现场4小时内到达", "售后", "必须响应", "生成售后服务方案、备品备件、本地响应和升级维护承诺", "第三章", "A", "4小时到场需交付/服务负责人确认"],
        ["REQ-013", "付款", "到货30%、验收20%、使用3个月45%、质保期结束5%", "商务", "响应/可谈判", "报价及合同条款按采购文件响应", "第三章", "A", "商务确认是否接受"],
        ["REQ-014", "评分", "价格30、商务13、技术等；类似业绩近3年每个2分最多8分", "得分策略", "重点优化", "准备4个以上近三年智慧食堂类似业绩；技术方案覆盖实施、质量、安全、测试、培训、售后", "第四章", "A/B", "业绩合同/验收证明需授权"],
        ["REQ-015", "接口开放", "免费二次开发，对接HIS、营养管理、门禁、停车等", "接口/开放能力", "高风险响应", "引用新奥开放能力、接口规范和重庆环卫集成材料，写清边界、前提和数据接口条件", "表格1", "A/B", "免费二次开发范围需商务/研发确认，不能无限承诺"],
    ]
    out = PACKAGE_ROOT / "02-response-matrix" / "requirement-response-matrix.csv"
    with out.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["id", "category", "requirement", "section", "response_level", "draft_strategy", "source", "evidence_level", "human_gate"])
        writer.writerows(rows)

    gaps = [
        ["GAP-001", "最终报价", "预算15万元，需含税含全部费用", "商务负责人确认报价、税率、付款条件", "P0"],
        ["GAP-002", "营业执照和信用截图", "资格审查硬性要求", "准备最新盖章件", "P0"],
        ["GAP-003", "检测报告/检验报告", "PC/手机端多项功能要求加盖公章检测报告", "确认可用报告名称、有效期、对应功能", "P0"],
        ["GAP-004", "软著证书", "公众号/小程序、人脸安全、营养膳食管理系统软著", "补证书扫描件并映射到评分项", "P0"],
        ["GAP-005", "硬件品牌型号", "称重设备、绑盘机、消费机、托盘", "确认最终型号、参数和厂家授权", "P0"],
        ["GAP-006", "利旧设备信息", "现有7台称重称、100个扫码盘对接或原价收回", "现场确认协议、品牌型号、接口和回收口径", "P0"],
        ["GAP-007", "4小时到场承诺", "售后硬性要求", "确认本地服务人员和备件库", "P1"],
        ["GAP-008", "免费二次开发范围", "对接HIS、门禁、停车等", "定义接口前提、标准接口、非标准开发边界", "P0"],
        ["GAP-009", "类似业绩", "近3年智慧食堂类似项目最多8分", "选择4个可外发授权案例", "P0"],
    ]
    out_gap = PACKAGE_ROOT / "02-response-matrix" / "evidence-gap-list.csv"
    with out_gap.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["id", "gap", "why_needed", "owner_action", "priority"])
        writer.writerows(gaps)


def score_history() -> list[SelectedHistory]:
    selected: list[SelectedHistory] = []
    with HISTORY_MANIFEST.open(encoding="utf-8-sig") as f:
        for row in csv.DictReader(f):
            if row.get("knowledge_status") != "extracted":
                continue
            md_path = REPO_ROOT / row["markdown_path"]
            if not md_path.exists():
                continue
            text = md_path.read_text(encoding="utf-8", errors="ignore")
            haystack = " ".join([row.get("relative_to_dest", ""), row.get("zip_internal_path", ""), text[:350000]])
            terms = [term for term in KEY_TERMS if term in haystack]
            if not terms:
                continue
            score = 0
            for term in terms:
                score += haystack.count(term) * TERM_WEIGHTS.get(term, 1)
            if "新奥" in haystack:
                score += 120
            if any(x in haystack for x in ["总体技术方案", "实施方法", "开放能力", "接口", "质量", "售后", "类似项目", "报价", "招标文件"]):
                score += 40
            selected.append(SelectedHistory(score=score, terms=terms, row=row))
    selected.sort(key=lambda x: x.score, reverse=True)
    return selected


def copy_selected_history(selected: list[SelectedHistory]) -> list[dict[str, str]]:
    rows: list[dict[str, str]] = []
    raw_total = 0
    raw_limit = 260 * 1024 * 1024
    raw_count = 0

    for index, item in enumerate(selected[:80], start=1):
        row = item.row
        md_src = REPO_ROOT / row["markdown_path"]
        md_dest_name = f"{index:03d}-{Path(row['markdown_path']).name}"
        md_dest = PACKAGE_ROOT / "03-rag-history-selected-markdown" / md_dest_name
        shutil.copy2(md_src, md_dest)

        raw_dest = ""
        raw_src = Path(row["extracted_path"])
        if raw_src.exists() and raw_count < 35:
            size = raw_src.stat().st_size
            if size < 60 * 1024 * 1024 and raw_total + size <= raw_limit:
                raw_count += 1
                raw_total += size
                raw_dest_path = PACKAGE_ROOT / "05-raw-selected-originals" / f"{raw_count:03d}-{raw_src.name}"
                shutil.copy2(raw_src, raw_dest_path)
                raw_dest = str(raw_dest_path.relative_to(PACKAGE_ROOT))

        rows.append(
            {
                "rank": str(index),
                "score": str(item.score),
                "matched_terms": "、".join(item.terms),
                "history_relative_path": row["relative_to_dest"],
                "markdown_in_package": str(md_dest.relative_to(PACKAGE_ROOT)),
                "raw_original_in_package": raw_dest,
                "raw_original_source": row["extracted_path"],
                "usage_hint": usage_hint(row["relative_to_dest"]),
            }
        )

    out = PACKAGE_ROOT / "06-indexes" / "selected-history-rag-index.csv"
    with out.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
        writer.writeheader()
        writer.writerows(rows)
    return rows


def usage_hint(path: str) -> str:
    if "报价" in path or "清单" in path:
        return "报价结构、设备参数、供货范围参考；最终价格需人工重算"
    if "技术方案" in path or "标书" in path:
        return "总体技术方案、章节结构和历史表述参考"
    if "实施" in path or "交付" in path or "安装" in path:
        return "实施计划、安装调试、培训和保障参考"
    if "接口" in path or "开放" in path or "兼容" in path:
        return "系统对接、开放能力和兼容性边界参考"
    if "质量" in path or "售后" in path or "质保" in path:
        return "质量保证、售后服务和质保承诺参考"
    if "类似" in path or "业绩" in path:
        return "类似业绩表述参考；外发前需授权和证据"
    return "历史投标内容参考"


def copy_internal_materials() -> list[dict[str, str]]:
    rows = []
    for index, rel in enumerate(INTERNAL_FILES, start=1):
        src = REPO_ROOT / rel
        if not src.exists():
            continue
        dest = PACKAGE_ROOT / "04-project-internal-materials" / f"{index:03d}-{src.name}"
        shutil.copy2(src, dest)
        rows.append(
            {
                "rank": str(index),
                "source_path": str(src),
                "package_path": str(dest.relative_to(PACKAGE_ROOT)),
                "usage_hint": "项目内智慧食堂产品、控标参数、实施/接口/历史方案材料",
                "sha256": sha256_file(src),
            }
        )
    out = PACKAGE_ROOT / "06-indexes" / "project-internal-material-index.csv"
    with out.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
        writer.writeheader()
        writer.writerows(rows)
    return rows


def write_prompt() -> None:
    prompt = """# 请基于资料包生成完整投标响应文件

你是投标文件编制专家，请基于我上传的资料包，为“国家康复辅具研究中心智慧食堂项目 / 智慧餐厅称重系统改造”生成完整投标响应文件草案。

## 必须先读的文件

1. `00-instructions/README.md`
2. `01-procurement-file/procurement-extracted.md`
3. `02-response-matrix/requirement-response-matrix.csv`
4. `02-response-matrix/evidence-gap-list.csv`
5. `06-indexes/selected-history-rag-index.csv`
6. `06-indexes/project-internal-material-index.csv`

## 输出目标

请生成一份可交给投标负责人继续编辑的完整响应文件草案，至少包含：

1. 封面与目录
2. 投标函
3. 报价总表和分项报价表（如资料中缺最终价格，请用“待商务确认”占位，不要编造）
4. 法定代表人身份证明/授权委托书模板
5. 资格证明与承诺函清单
6. 商务响应表
7. 技术响应表
8. 技术方案
9. 项目实施方案
10. 质量保障、安全保障、安装调试、测试验收、培训方案
11. 售后服务方案
12. 质保期承诺书
13. 类似业绩表
14. 软著、检测报告、厂家授权、信用截图等附件目录
15. 偏离表和补证清单

## 写作规则

- 所有硬性要求必须来自采购文件或资料包，不得编造。
- 历史标书资料只能作为内部参考，不得直接承诺未被本项目确认的价格、品牌型号、证书、授权、案例或交付能力。
- 预算为 15 万含税，报价不得超过预算；最终报价请标注“待商务确认”。
- 对接 HIS、营养管理、门禁、停车等系统时，必须写清前置条件：对方开放接口、提供接口文档、联调环境、测试账号、数据权限和验收标准；不要写成无限免费开发。
- 对检测报告、检验报告、软著证书、信用截图、厂家授权、类似业绩证明，统一放入“待补附件清单”，不要编造证书编号。
- 4小时到场、7*24响应、质保期、付款条件等商务承诺需要单独列出，方便人工复核。
- 输出时先给“投标文件总目录”，再逐章写正文。

## 优先参考方向

- 称重设备、绑盘机、托盘、智能餐线、断网离线、压秤报警、防逃费：优先参考 `03-rag-history-selected-markdown` 中新奥智能餐线相关材料。
- 技术方案、实施方案、售后培训：参考历史投标文件和 `04-project-internal-materials` 中保康中学、重庆环卫、苏州福耀材料。
- 智慧食堂 PC/手机端功能、营养健康分析、软著/检测报告映射：参考产品控标参数库和项目内智慧食堂资料。

## 输出格式

请先输出 Markdown 版本。每章末尾增加“引用资料与待人工确认项”。最后单独输出：

1. `投标材料补证清单`
2. `人工审核清单`
3. `不应直接承诺的高风险表述清单`
"""
    (PACKAGE_ROOT / "07-chatgpt-prompts" / "chatgpt-bid-generation-prompt.md").write_text(prompt, encoding="utf-8")

    quick = """请读取我上传的资料包，先不要直接写完整标书。请先输出：
1. 你理解的项目采购需求摘要；
2. 评分策略；
3. 响应文件目录；
4. 必须补证的材料清单；
5. 你准备引用哪些历史材料。
等我确认后，再生成完整投标响应文件。"""
    (PACKAGE_ROOT / "07-chatgpt-prompts" / "chatgpt-first-message.md").write_text(quick, encoding="utf-8")


def write_readme(history_rows: list[dict[str, str]], internal_rows: list[dict[str, str]], procurement_meta: dict[str, object]) -> None:
    readme = f"""# 国家康复辅具研究中心智慧餐厅称重系统投标资料包

生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

## 用途

本资料包用于上传给 ChatGPT，让 ChatGPT 基于采购文件、历史标书 RAG 抽取资料和项目内智慧食堂资料，生成本项目投标响应文件草案。

## ChatGPT 上传限制

根据 OpenAI Help Center 的 File Uploads FAQ，ChatGPT/GPT 单文件硬限制为 512MB；文本文档有 200 万 token 上限；CSV/表格也有约 50MB 级别限制。为降低失败概率，本包已拆成少数几个 zip，均控制在 512MB 以下。

官方说明：https://help.openai.com/en/articles/8555545-file-uploads-faq

## 推荐上传顺序

1. 先上传 `rehab-aids-bid-core-chatgpt.zip`
2. 再上传 `rehab-aids-bid-raw-selected-originals.zip`
3. 最后把 `07-chatgpt-prompts/chatgpt-bid-generation-prompt.md` 的内容复制给 ChatGPT。

如果 ChatGPT 提示文件过多或太大，只上传 core zip 即可。core zip 已包含采购文件、响应矩阵、关键历史 RAG 文本、项目内资料和 prompt。

## 包内结构

| 目录 | 内容 |
| --- | --- |
| `00-instructions/` | 给人的说明和风险边界 |
| `01-procurement-file/` | 本项目采购文件原件、抽取 Markdown、表格 CSV |
| `02-response-matrix/` | 要求响应矩阵和补证清单 |
| `03-rag-history-selected-markdown/` | 从历史标书 RAG 中按关键词筛选的参考 Markdown |
| `04-project-internal-materials/` | 本项目内智慧食堂控标、方案、实施、接口、售前资料 |
| `05-raw-selected-originals/` | 筛选后的历史原始参考文件，体积受控 |
| `06-indexes/` | 材料索引、来源、用途建议 |
| `07-chatgpt-prompts/` | 投喂 ChatGPT 的 prompt |
| `08-output-skeleton/` | 建议输出目录骨架 |

## 本轮抽取结果

- 采购文件段落行数：{procurement_meta['paragraph_lines']}
- 采购文件表格数：{procurement_meta['tables']}
- 历史 RAG 参考 Markdown：{len(history_rows)} 份
- 项目内参考材料：{len(internal_rows)} 份

## 人工门禁

- 报价、税率、付款条款、合同条款必须商务确认。
- 设备品牌型号、厂家授权、检测报告、软著、信用截图、类似业绩证明必须人工补附件。
- HIS、营养管理、门禁、停车等接口对接不能写成无限免费开发，必须限定接口开放、文档、联调环境、测试账号和验收标准。
- 历史标书资料默认内部参考，正式外发前必须脱敏和确认授权。
"""
    (PACKAGE_ROOT / "00-instructions" / "README.md").write_text(readme, encoding="utf-8")


def write_skeleton() -> None:
    for rel in [
        "01-cover-and-toc.md",
        "02-business-response.md",
        "03-technical-response.md",
        "04-implementation-plan.md",
        "05-service-training-warranty.md",
        "06-attachments-checklist.md",
        "07-deviation-and-risk-list.md",
    ]:
        (PACKAGE_ROOT / "08-output-skeleton" / rel).write_text(f"# {rel}\n\n待 ChatGPT 生成。\n", encoding="utf-8")


def write_manifest() -> None:
    rows = []
    for p in sorted(PACKAGE_ROOT.rglob("*")):
        if p.is_file() and p.suffix != ".zip":
            rows.append(
                {
                    "path": str(p.relative_to(PACKAGE_ROOT)),
                    "size_bytes": str(p.stat().st_size),
                    "sha256": sha256_file(p),
                }
            )
    out = PACKAGE_ROOT / "06-indexes" / "package-file-manifest.csv"
    with out.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["path", "size_bytes", "sha256"])
        writer.writeheader()
        writer.writerows(rows)


def zip_dir(zip_path: Path, include_dirs: list[str]) -> None:
    if zip_path.exists():
        zip_path.unlink()
    with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=6) as zf:
        for d in include_dirs:
            root = PACKAGE_ROOT / d
            if not root.exists():
                continue
            for p in root.rglob("*"):
                if p.is_file():
                    zf.write(p, p.relative_to(PACKAGE_ROOT))


def write_summary(history_rows: list[dict[str, str]], internal_rows: list[dict[str, str]], procurement_meta: dict[str, object]) -> None:
    zip_info = []
    for p in sorted(PACKAGE_ROOT.glob("*.zip")):
        zip_info.append({"name": p.name, "size_bytes": p.stat().st_size, "sha256": sha256_file(p)})
    summary = {
        "generated_at": datetime.now().isoformat(timespec="seconds"),
        "package_root": str(PACKAGE_ROOT),
        "procurement": procurement_meta,
        "selected_history_count": len(history_rows),
        "internal_material_count": len(internal_rows),
        "zips": zip_info,
    }
    (TASK_DIR / "pack-build-result.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
    lines = [
        "# 国家康复辅具研究中心智慧餐厅称重系统投标资料包生成记录",
        "",
        f"- 桌面资料包：`{PACKAGE_ROOT}`",
        f"- 采购文件：`{PROCUREMENT_DOCX}`",
        f"- 历史 RAG 参考 Markdown：{len(history_rows)} 份",
        f"- 项目内参考材料：{len(internal_rows)} 份",
        "",
        "## ZIP",
        "",
    ]
    for item in zip_info:
        lines.append(f"- `{item['name']}`：{item['size_bytes'] / 1024 / 1024:.1f}MB，SHA256 `{item['sha256']}`")
    lines.extend(
        [
            "",
            "## 边界",
            "",
            "- 本包用于 ChatGPT 生成内部标书草案，不是最终可外发投标文件。",
            "- 价格、证书、授权、检测报告、类似业绩、接口免费开发范围和 4 小时到场承诺必须人工复核。",
        ]
    )
    (TASK_DIR / "summary.md").write_text("\n".join(lines) + "\n", encoding="utf-8")


def main() -> None:
    clean_package_root()
    procurement_meta = extract_procurement()
    write_requirement_matrix()
    selected = score_history()
    history_rows = copy_selected_history(selected)
    internal_rows = copy_internal_materials()
    write_prompt()
    write_skeleton()
    write_readme(history_rows, internal_rows, procurement_meta)
    write_manifest()

    zip_dir(
        PACKAGE_ROOT / "rehab-aids-bid-core-chatgpt.zip",
        [
            "00-instructions",
            "01-procurement-file",
            "02-response-matrix",
            "03-rag-history-selected-markdown",
            "04-project-internal-materials",
            "06-indexes",
            "07-chatgpt-prompts",
            "08-output-skeleton",
        ],
    )
    zip_dir(PACKAGE_ROOT / "rehab-aids-bid-raw-selected-originals.zip", ["05-raw-selected-originals", "06-indexes"])
    zip_dir(
        PACKAGE_ROOT / "rehab-aids-bid-all-in-one-chatgpt.zip",
        [
            "00-instructions",
            "01-procurement-file",
            "02-response-matrix",
            "03-rag-history-selected-markdown",
            "04-project-internal-materials",
            "05-raw-selected-originals",
            "06-indexes",
            "07-chatgpt-prompts",
            "08-output-skeleton",
        ],
    )
    write_summary(history_rows, internal_rows, procurement_meta)


if __name__ == "__main__":
    main()
