from __future__ import annotations

import csv
import html
import json
import shutil
import textwrap
import zipfile
from datetime import datetime
from pathlib import Path

from openpyxl import load_workbook


ROOT = Path("/Users/jack/code/010-cpt/008-zhct/zhctprompt")
SOURCE_XLSX = Path(
    "/Users/jack/同步空间/cpt/03_团队组织与人员管理/003_人员管理/099 我的资料/"
    "2026年高管目标责任书&绩效考核/2026年数字体育科技事业部高管目标责任书-姜阳.xlsx"
)
TASK_ID = "ZHCTPROMPT-20260628-PERFORMANCE-Q1Q2-EVIDENCE"
OUT = ROOT / "work/2026-06-28-personal-performance-q1q2-evidence-pack"
UPLOAD = OUT / "upload-to-chatgpt"
SELECTED = UPLOAD / "selected-source-files"
DESKTOP = Path("/Users/jack/Desktop/姜阳2026绩效Q1Q2达标评估素材包_20260628")
DESKTOP_FINAL = DESKTOP / "01-最终交付"
DESKTOP_UPLOAD = DESKTOP / "02-给ChatGPT上传"
THREAD_RECORD = (
    ROOT
    / "work/team-learning/thread-records/2026-06-28/personal-performance-q1q2-evidence-pack.md"
)


TASKS = [
    {
        "task_id": "KPI-FIN-01",
        "quarter": "Q1/Q2",
        "indicator": "公司业绩：全司收入、全司利润",
        "target": "Q1 收入 19563 万、利润 -3600 万；Q2 收入 45100 万、利润 1800 万",
        "scoring": "财务管理中心确认；利润为否决项",
        "evidence_needed": "财务中心经营结果、公司利润确认口径",
        "judgement_hint": "本包仅列目标，不判断达标。",
    },
    {
        "task_id": "KPI-FIN-02",
        "quarter": "Q1/Q2",
        "indicator": "部门财务：考核利润额、销售收入、毛利率",
        "target": "Q1 利润 -191.2923 万、收入 810 万、毛利率 40.37%；Q2 利润 55.9831 万、收入 1120 万、毛利率 51.33%",
        "scoring": "财务管理中心确认；达标 100 分，80%-100% 按完成率",
        "evidence_needed": "事业部销售、利润、毛利率财务表",
        "judgement_hint": "需财务数据补证，项目材料只能解释经营动作。",
    },
    {
        "task_id": "KPI-PD-Q1-01",
        "quarter": "Q1",
        "indicator": "产品设计：用户研究与报告",
        "target": "访谈 >=3 次；报告 1 份",
        "scoring": "全年 >=12 次结构化访谈、>=4 份调研/竞品报告，占产品设计评分 20%",
        "evidence_needed": "访谈记录、调研报告、竞品报告、来源索引",
        "judgement_hint": "当前有调研/竞品材料线索，但访谈次数需签到或纪要补证。",
    },
    {
        "task_id": "KPI-PD-Q1-02",
        "quarter": "Q1",
        "indicator": "产品设计：路线图/版本规划",
        "target": "形成路线图/版本规划",
        "scoring": "方案规划覆盖规划-设计-实现-迭代，全年 >=3 份新模块 PRD/原型并评审，占产品设计评分 40%",
        "evidence_needed": "产品路线图、版本规划、产品地图、战略规划",
        "judgement_hint": "项目内有智慧食堂产品地图、路线图和战略材料。",
    },
    {
        "task_id": "KPI-PD-Q2-01",
        "quarter": "Q2",
        "indicator": "产品设计：用户研究与报告",
        "target": "访谈 >=3 次；报告 1 份",
        "scoring": "同 KPI-PD-Q1-01",
        "evidence_needed": "访谈记录、市场调研、痛点报告、竞品对比",
        "judgement_hint": "Q2 有市场/痛点/竞品报告线索；访谈次数仍需原始记录补证。",
    },
    {
        "task_id": "KPI-PD-Q2-02",
        "quarter": "Q2",
        "indicator": "产品设计：模块方案",
        "target": "模块方案 1 份",
        "scoring": "新模块 PRD/原型/方案并组织评审",
        "evidence_needed": "模块方案、PRD、原型、评审 HTML",
        "judgement_hint": "Q2 有 QC、平安云厨、教委/食安/智慧食堂多项方案与原型材料。",
    },
    {
        "task_id": "KPI-PD-Q1Q2-03",
        "quarter": "Q1/Q2",
        "indicator": "产品设计：关键文档齐套率 100%",
        "target": "Q1/Q2 关键文档齐套率 100%",
        "scoring": "产品说明书、技术参数、售前资料按季度更新，关键文档齐套率 100%，占产品设计评分 40%",
        "evidence_needed": "产品说明书、技术参数、售前资料、投标/方案素材包、source-index",
        "judgement_hint": "项目内有大量售前、产品、投标、手册和 HTML review 材料，但需定义齐套清单后逐项勾稽。",
    },
    {
        "task_id": "KPI-DEV-Q1Q2-01",
        "quarter": "Q1/Q2",
        "indicator": "产品开发：响应达标率 >=90%",
        "target": "响应时效 1 小时内、需求理解率、实现准确度三指标达标率 >=90%",
        "scoring": "占产品开发评分 30%",
        "evidence_needed": "云效任务、响应记录、需求评审记录、代码提交/MR、测试结果",
        "judgement_hint": "任务索引和代码 summary 能证明处理事项，达标率需从云效/服务台统计。",
    },
    {
        "task_id": "KPI-DEV-Q1Q2-02",
        "quarter": "Q1/Q2",
        "indicator": "产品开发：>=2 套可复用技术模块",
        "target": "每季度 >=2 套可复用技术模块；线上无 P0",
        "scoring": "可复用技术模块与性能优化占产品开发评分 30%",
        "evidence_needed": "可复用模块说明、测试/发布文档、复用记录",
        "judgement_hint": "XlsxStreamWriter、Apifox 测试矩阵、QC 采集链路、AI Native skill/workflow 等可作为候选，需确认哪些按技术模块认定。",
    },
    {
        "task_id": "KPI-DEV-Q1Q2-03",
        "quarter": "Q1/Q2",
        "indicator": "产品开发：按时交付、BUG 率、测试发布文档",
        "target": "项目交付按时率 95%；BUG 率 <3%；按规范提交测试、发布文档；线上无 P0",
        "scoring": "占产品开发评分 40%；P0 得分为 0，其他事故另扣分",
        "evidence_needed": "测试报告、发布文档、缺陷台账、线上事故台账",
        "judgement_hint": "项目内可找到多项测试/验证 summary，但 BUG 率与事故级别需统一台账。",
    },
    {
        "task_id": "KPI-DEL-Q1Q2-01",
        "quarter": "Q1/Q2",
        "indicator": "产品交付：响应及时率与按期交付率",
        "target": "交付响应及时率 >=95%；项目按期交付率 >=95%",
        "scoring": "占部门业绩 15%；未达标按达成率",
        "evidence_needed": "交付项目台账、验收计划、上线/验收记录、服务响应记录",
        "judgement_hint": "项目材料能证明交付资产化动作，95% 指标需交付台账补证。",
    },
    {
        "task_id": "KPI-MGT-Q1Q2-01",
        "quarter": "Q1/Q2",
        "indicator": "团队管理：降本增效",
        "target": "Q1 人力成本 <=84.38 万、现金费用 <=11 万；Q2 人力成本 <=75 万、现金费用 <=11 万",
        "scoring": "财务确认；年度人力成本 <=300 万且现金费用 <=44 万",
        "evidence_needed": "财务中心人力成本、现金费用表",
        "judgement_hint": "本包列待补证项，不用项目文件替代财务口径。",
    },
    {
        "task_id": "KPI-COMPLIANCE-Q1Q2-01",
        "quarter": "Q1/Q2",
        "indicator": "规范化运营减分项",
        "target": "不出现内审监察通报、客户投诉、违规宣传、用工风险、违反规章制度等减分项",
        "scoring": "财务/法务/人力/运营等按影响程度扣分，不封顶",
        "evidence_needed": "内审、法务、人力、运营通报；客户投诉记录",
        "judgement_hint": "本包不含减分项系统记录，需职能部门确认。",
    },
]


SEED_EVIDENCE = [
    ("E01", "绩效原始表", SOURCE_XLSX, "A", "Q1/Q2 目标责任书原始来源"),
    ("E02", "高管半年度述职资料包", ROOT / "work/2026-06-27-executive-midyear-ppt-plan/summary.md", "B", "已汇总上半年成果、偏差、下半年规划，并明确待补证项"),
    ("E03", "高管述职来源索引", ROOT / "work/2026-06-27-executive-midyear-ppt-plan/source-index.csv", "B", "述职资料使用的项目内素材来源"),
    ("E04", "产品研发今年版", ROOT / "work/2026-06-22-digital-business-meeting-minutes-strategy/template-adapted-report/final/product-rd-2026-current-mainline.md", "B", "技术储备产品化、硬件横评、AI Native、模块复用率等主线"),
    ("E05", "数科会议历史与策略来源索引", ROOT / "work/2026-06-22-digital-business-meeting-minutes-strategy/source-index.csv", "B", "数科会议纪要和产品策略来源"),
    ("E06", "智慧食堂市场调研草稿", ROOT / "work/2026-06-15-smart-canteen-market-research/smart-canteen-market-research.md", "C/B", "市场细分、用户画像、竞品对比；文件自身标注需被方法论纠偏"),
    ("E07", "智慧市场痛点首轮报告", ROOT / "work/2026-06-18-consumer-pain-point-smart-market/smart-market-pain-point-discovery-report.md", "B/C/D", "痛点雷达、角色假设和访谈问题"),
    ("E08", "产品标准化与 AI Native 周报素材", ROOT / "work/2026-05-29-product-standardization-ai-native-weekly-material/weekly-material.md", "B/D", "项目标准化/定制化比例、三套系统完善度和 AI 定制提效"),
    ("E09", "项目标准化明细表", ROOT / "work/2026-05-29-product-standardization-ai-native-weekly-material/project-standardization-map.csv", "B/D", "样板项目标准化/定制化估算"),
    ("E10", "智慧食堂产品地图输出", ROOT / "work/2026-05-17-smart-canteen-product-map-excel/summary.md", "B", "产品地图和下一步行动清单"),
    ("E11", "智慧食堂下一步行动清单", ROOT / "work/2026-05-17-smart-canteen-product-map-excel/outputs/scenario_product_plan__03下一步行动.csv", "B", "平台架构、账户闭环、设备联调、反馈闭环等路线图行动"),
    ("E12", "智慧食堂 KANO 产品挖掘", ROOT / "work/2026-06-09-youdao-chat-model-smart-canteen-report/smart-canteen-kano-product-mining.md", "B/C", "需求分层、证据和缺口"),
    ("E13", "智慧食堂 PMF 产品挖掘", ROOT / "work/2026-06-09-youdao-chat-model-smart-canteen-report/smart-canteen-pmf-product-mining.md", "B/C", "标准包、体验路径、验证指标"),
    ("E14", "智慧食堂战略 V0.3", ROOT / "standards-stack/product-strategy/smart-canteen/SMART_CANTEEN_STRATEGY_V0.3.md", "B/D", "产品战略、路线图和待补证边界"),
    ("E15", "智慧食堂路线图", ROOT / "standards-stack/product-strategy/smart-canteen/ROADMAP_V0.2.csv", "B/D", "30-90 天标准包、90-180 天增值包、180-365 天经营化路线"),
    ("E16", "平安云厨 PRD", ROOT / "product_pingan_yunchu/product-solutions/current/01-product-solution/02-项目PRD.md", "B", "PRD 与产品全流程设计素材"),
    ("E17", "平安云厨原型资产清单", ROOT / "product_pingan_yunchu/product-design/ui-prototypes/current/10-design-product-prototype-index/02-原型资产清单.md", "B", "原型和设计资产齐套线索"),
    ("E18", "QC 产品介绍", ROOT / "work_qc/product-introduction/summary.md", "A/B/D", "售前轻采集系统入口、功能更新和边界"),
    ("E19", "QC L0 测试摘要", ROOT / "work_qc/automated-testing/2026-06-22-login-collection-export-smoke/run-summary.md", "B", "QC 登录到导出链路的测试尝试与 blocker"),
    ("E20", "Apifox 接口快照", ROOT / "work_store/apifox/2026-05-08-smart-canteen/summary.md", "B", "559 个 API、25 环境、测试场景/套件快照"),
    ("E21", "Apifox 测试矩阵执行摘要", ROOT / "work_store/apifox/2026-05-08-queue-execution/reports/summary.md", "B", "7826 条接口测试矩阵、44 场景、8 套件"),
    ("E22", "VWCG-915 流式 XLSX 模块", ROOT / "work_store/VWCG-915-async-export-streaming-xlsx-20260508/reports/summary.md", "B", "可复用 XlsxStreamWriter 模块与验证"),
    ("E23", "Store 登录订单 smoke 阻塞记录", ROOT / "work_store/automated-testing/2026-06-22-login-order-query-smoke/run-summary.md", "B", "运行环境 blocker，不作为产品缺陷"),
    ("E24", "W24 团队周汇总", ROOT / "work/weekly-reports/outputs/2026-W24/team-weekly-summary.md", "B", "W24 产品、投标、QA Gate、知识库和风险"),
    ("E25", "W25 团队周汇总", ROOT / "work/weekly-reports/outputs/2026-W25/team-weekly-summary.md", "B", "W25 PRD 闭环、需求挖掘、招投标 shadow run"),
    ("E26", "任务队列", ROOT / "tasks/QUEUE.md", "B", "项目内已完成和待办索引"),
    ("E27", "任务索引聚合", ROOT / "control/task-index/tasks.tsv", "B", "云效、MR、证据路径和任务状态聚合"),
]


TASK_EVIDENCE_MAP = {
    "KPI-FIN-01": ["E01"],
    "KPI-FIN-02": ["E01", "E02", "E04", "E08"],
    "KPI-PD-Q1-01": ["E06", "E07", "E12", "E13"],
    "KPI-PD-Q1-02": ["E10", "E11", "E14", "E15"],
    "KPI-PD-Q2-01": ["E06", "E07", "E12", "E13"],
    "KPI-PD-Q2-02": ["E04", "E16", "E17", "E18"],
    "KPI-PD-Q1Q2-03": ["E02", "E03", "E04", "E08", "E16", "E17", "E18", "E24", "E25"],
    "KPI-DEV-Q1Q2-01": ["E20", "E21", "E22", "E24", "E25", "E26", "E27"],
    "KPI-DEV-Q1Q2-02": ["E20", "E21", "E22", "E18", "E19", "E24", "E25"],
    "KPI-DEV-Q1Q2-03": ["E19", "E20", "E21", "E22", "E23", "E26", "E27"],
    "KPI-DEL-Q1Q2-01": ["E02", "E08", "E13", "E18", "E24", "E25"],
    "KPI-MGT-Q1Q2-01": ["E01", "E02", "E04", "E08"],
    "KPI-COMPLIANCE-Q1Q2-01": ["E01", "E26", "E27"],
}


def ensure_dirs() -> None:
    for path in [OUT, UPLOAD, SELECTED, DESKTOP_FINAL, DESKTOP_UPLOAD, THREAD_RECORD.parent]:
        path.mkdir(parents=True, exist_ok=True)


def extract_workbook() -> list[dict[str, str]]:
    wb = load_workbook(SOURCE_XLSX, data_only=True)
    ws = wb.active
    rows = []
    for r in range(1, ws.max_row + 1):
        values = []
        for c in range(1, ws.max_column + 1):
            value = ws.cell(r, c).value
            values.append("" if value is None else str(value).replace("\n", " / "))
        rows.append({"row": str(r), **{f"col_{i+1}": v for i, v in enumerate(values)}})
    return rows


def write_csv(path: Path, rows: list[dict[str, str]], fieldnames: list[str]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        for row in rows:
            writer.writerow({key: row.get(key, "") for key in fieldnames})


def short_snippet(path: Path, keywords: list[str]) -> str:
    if not path.exists() or path.is_dir():
        return "SOURCE_MISSING"
    if path.suffix.lower() == ".xlsx":
        return "原始绩效 Excel，见任务清单抽取。"
    if path.stat().st_size > 1_500_000:
        return "文件较大，本包只引用路径，不复制全文。"
    text = path.read_text(encoding="utf-8", errors="ignore")
    compact = " ".join(line.strip() for line in text.splitlines() if line.strip())
    for keyword in keywords:
        idx = compact.find(keyword)
        if idx >= 0:
            start = max(0, idx - 120)
            end = min(len(compact), idx + 360)
            return compact[start:end]
    return compact[:480]


def copy_selected_sources() -> list[dict[str, str]]:
    manifest = []
    for evidence_id, title, path, level, note in SEED_EVIDENCE:
        rel = ""
        copied = ""
        if path.exists() and path.suffix.lower() != ".xlsx" and path.stat().st_size <= 1_500_000:
            safe_name = f"{evidence_id}_{path.name}".replace("/", "_")
            target = SELECTED / safe_name
            if path.suffix.lower() in {".md", ".csv", ".tsv", ".txt", ".html"}:
                text = path.read_text(encoding="utf-8", errors="ignore")
                cleaned = "\n".join(line.rstrip() for line in text.splitlines()).rstrip() + "\n"
                target.write_text(cleaned, encoding="utf-8")
            else:
                shutil.copy2(path, target)
            rel = str(target.relative_to(UPLOAD))
            copied = "yes"
        else:
            copied = "no"
        manifest.append(
            {
                "evidence_id": evidence_id,
                "title": title,
                "evidence_level": level,
                "source_path": str(path),
                "copied_to_upload_pack": copied,
                "upload_relative_path": rel,
                "note": note,
            }
        )
    return manifest


def build_task_evidence_rows(manifest_by_id: dict[str, dict[str, str]]) -> list[dict[str, str]]:
    rows = []
    keywords = ["访谈", "报告", "路线图", "PRD", "原型", "测试", "交付", "AI", "标准化", "成本"]
    for task in TASKS:
        evidence_ids = TASK_EVIDENCE_MAP[task["task_id"]]
        for eid in evidence_ids:
            item = manifest_by_id[eid]
            rows.append(
                {
                    "task_id": task["task_id"],
                    "quarter": task["quarter"],
                    "indicator": task["indicator"],
                    "target": task["target"],
                    "evidence_id": eid,
                    "evidence_title": item["title"],
                    "evidence_level": item["evidence_level"],
                    "source_path": item["source_path"],
                    "copied_to_upload_pack": item["copied_to_upload_pack"],
                    "why_relevant": item["note"],
                    "snippet": short_snippet(Path(item["source_path"]), keywords),
                    "gap_or_caution": task["judgement_hint"],
                }
            )
    return rows


def markdown_table(rows: list[dict[str, str]], cols: list[str]) -> str:
    def clean(value: str) -> str:
        return str(value).replace("|", "\\|").replace("\n", " ")

    out = ["| " + " | ".join(cols) + " |", "| " + " | ".join(["---"] * len(cols)) + " |"]
    for row in rows:
        out.append("| " + " | ".join(clean(row.get(col, "")) for col in cols) + " |")
    return "\n".join(out)


def build_markdown(task_rows: list[dict[str, str]], evidence_rows: list[dict[str, str]], manifest: list[dict[str, str]]) -> str:
    task_short = [
        {
            "task_id": r["task_id"],
            "quarter": r["quarter"],
            "indicator": r["indicator"],
            "target": r["target"],
            "evidence_needed": r["evidence_needed"],
            "judgement_hint": r["judgement_hint"],
        }
        for r in task_rows
    ]
    high_signal = evidence_rows[:]
    md = f"""# 姜阳 2026 绩效 Q1/Q2 达标评估素材包

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

## 用途

这份材料包从《2026年数字体育科技事业部高管目标责任书-姜阳.xlsx》中抽取 Q1/Q2 可核验任务，并把 `zhctprompt` 项目内可追溯素材映射到每个任务，方便后续上传给 ChatGPT 判断“达标、部分达标、未达标、待补证”。

## 重要边界

- 本包不直接替你打绩效分，只做任务拆解和证据素材索引。
- 财务收入、利润、毛利、人力成本、现金费用必须以财务管理中心口径为准。
- 访谈次数、交付响应及时率、按期交付率、BUG 率、线上事故等级，仍需云效、交付台账、访谈签到/纪要或职能部门记录补 A/B 级证据。
- 项目内 Markdown/CSV/HTML 多为 B/C/D 级内部证据，可支撑“做了什么、形成了什么资产”，不能替代财务或客户正式验收。

## Q1/Q2 点对点任务清单

{markdown_table(task_short, ["task_id", "quarter", "indicator", "target", "evidence_needed", "judgement_hint"])}

## 证据素材清单

{markdown_table(manifest, ["evidence_id", "title", "evidence_level", "source_path", "copied_to_upload_pack", "note"])}

## 任务到证据映射

{markdown_table(high_signal, ["task_id", "evidence_id", "evidence_title", "evidence_level", "why_relevant", "gap_or_caution"])}

## 建议给 ChatGPT 的判断方法

1. 先按任务逐项判断，不要先给总分。
2. 对每项给四档：`达标`、`基本达标但需补证`、`部分达标`、`无法判断/待补证`。
3. 财务类只列待补证，不用项目材料推断财务结果。
4. 产品设计类重点看是否有访谈/报告、路线图/模块方案、PRD/原型/售前资料齐套。
5. 产品开发类区分“有项目/模块/测试材料”和“达标率/BUG率/P0事故需台账”。
6. 产品交付类必须要求交付台账或验收记录，不能只看材料数量。
7. 管理和规范化运营类必须要求财务/职能部门确认。

## ChatGPT 可直接使用的提示词

```text
请基于我上传的绩效目标抽取表、任务-证据映射表和素材文件，判断姜阳 2026 年 Q1/Q2 绩效任务达标情况。

要求：
1. 不要直接给总分，先按 task_id 逐项判断。
2. 每项输出：目标、已找到证据、证据等级、达标判断、缺口、需要我补充的材料。
3. 财务类、人力成本、现金费用、交付响应率、按期交付率、BUG率、线上事故等级，如果没有 A/B 级正式台账，不要臆测达标。
4. 对项目内 Markdown/CSV/HTML，按内部过程证据处理；能证明资产和动作，不能自动证明财务、客户验收或量化达标。
5. 最后给一个“最可能已达标项 / 需补证项 / 高风险未达标项”的三列表。
```
"""
    return md


def build_html(md: str, path: Path) -> None:
    body = html.escape(md)
    # Lightweight markdown-ish rendering for review without external deps.
    body = body.replace("&quot;", '"')
    lines = []
    in_code = False
    for line in body.splitlines():
        if line.startswith("```"):
            lines.append("</code></pre>" if in_code else "<pre><code>")
            in_code = not in_code
        elif in_code:
            lines.append(line)
        elif line.startswith("# "):
            lines.append(f"<h1>{line[2:]}</h1>")
        elif line.startswith("## "):
            lines.append(f"<h2>{line[3:]}</h2>")
        elif line.startswith("- "):
            lines.append(f"<p class='bullet'>• {line[2:]}</p>")
        elif line.startswith("| "):
            lines.append(f"<p class='tableline'>{line}</p>")
        elif not line.strip():
            lines.append("")
        else:
            lines.append(f"<p>{line}</p>")
    html_doc = f"""<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>姜阳 2026 绩效 Q1/Q2 达标评估素材包</title>
  <style>
    body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; margin: 0; background: #f6f7f9; color: #172033; }}
    main {{ max-width: 1120px; margin: 0 auto; padding: 40px 28px 72px; }}
    h1 {{ font-size: 32px; margin: 0 0 18px; }}
    h2 {{ margin-top: 34px; padding-top: 18px; border-top: 1px solid #d9dee8; font-size: 22px; }}
    p {{ line-height: 1.72; margin: 10px 0; }}
    pre {{ background: #101828; color: #eef4ff; padding: 18px; overflow: auto; border-radius: 8px; }}
    .bullet {{ padding-left: 12px; }}
    .tableline {{ font-family: ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; background: #fff; border: 1px solid #e3e7ee; padding: 8px 10px; border-radius: 6px; font-size: 13px; }}
  </style>
</head>
<body><main>{chr(10).join(lines)}</main></body></html>
"""
    path.write_text(html_doc, encoding="utf-8")


def zip_dir(source: Path, target: Path) -> None:
    with zipfile.ZipFile(target, "w", compression=zipfile.ZIP_DEFLATED) as zf:
        for path in source.rglob("*"):
            if path.is_file():
                zf.write(path, path.relative_to(source))


def write_task_index() -> None:
    item = ROOT / f"control/task-index/items/{TASK_ID}.tsv"
    item.parent.mkdir(parents=True, exist_ok=True)
    header = [
        "task_id",
        "title",
        "source",
        "target_repo",
        "target_branch",
        "task_branch",
        "commit",
        "mr_url",
        "status",
        "evidence_path",
        "skills",
        "tags",
        "updated_at",
    ]
    row = {
        "task_id": TASK_ID,
        "title": "姜阳 2026 绩效 Q1/Q2 点对点任务与证据素材包",
        "source": str(SOURCE_XLSX),
        "target_repo": "zhctprompt",
        "target_branch": "master",
        "task_branch": "n/a",
        "commit": "n/a",
        "mr_url": "n/a",
        "status": "review-ready",
        "evidence_path": str(OUT.relative_to(ROOT)),
        "skills": "zhctprompt-self-update-on-qa;Spreadsheets;output-format-preferences;team-document-review-learning-loop;team-thread-markdown-record;task-index-library-maintenance",
        "tags": "performance;q1;q2;evidence-pack;chatgpt",
        "updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
    }
    with item.open("w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=header, delimiter="\t", lineterminator="\n")
        writer.writeheader()
        writer.writerow(row)


def write_thread_record() -> None:
    content = f"""# Personal Performance Q1/Q2 Evidence Pack

## Thread

- Date: 2026-06-28
- Owner: 姜阳
- Task: {TASK_ID}

## Goal

从姜阳个人绩效目标责任书中抽取 Q1/Q2 点对点任务，并找到本地项目内可用于 ChatGPT 判断达标情况的素材。

## Context

- Source Excel: `{SOURCE_XLSX}`
- Control project: `{ROOT}`
- Evidence output: `{OUT}`
- Desktop package: `{DESKTOP}`

## Outputs

- AI-readable summary: `work/2026-06-28-personal-performance-q1q2-evidence-pack/performance-q1q2-evidence-pack.md`
- Human review HTML: `work/2026-06-28-personal-performance-q1q2-evidence-pack/performance-q1q2-evidence-pack.html`
- Task list CSV: `work/2026-06-28-personal-performance-q1q2-evidence-pack/performance-q1q2-task-list.csv`
- Evidence map CSV: `work/2026-06-28-personal-performance-q1q2-evidence-pack/task-evidence-map.csv`
- ChatGPT upload ZIP: `work/2026-06-28-personal-performance-q1q2-evidence-pack/jiangyang-performance-q1q2-chatgpt-pack-20260628.zip`

## Evidence

- A-level source: personal KPI Excel.
- B/C/D internal project evidence: summaries, source indexes, product strategy, weekly reports, QC/Apifox/store task records.
- Blockers: financial achievement, human cost, cash expense, response rate, on-time delivery rate, bug rate, online incident level and interview counts need formal ledgers or raw meeting records.

## Reusable Learning

绩效达标判断应先拆任务，再映射证据等级。项目文档能证明资产和动作，不能替代财务、交付、质量和人力正式口径。

## Anti-Patterns

- 不用周报材料臆测财务达标。
- 不把战略稿里的“待补数据”写成已达标事实。
- 不用项目材料数量替代访谈次数、交付及时率或 BUG 率。

## Training Notes

后续类似任务优先输出 ChatGPT 上传包：绩效目标抽取、任务清单、证据索引、证据等级、缺口清单和评估提示词。

## Follow-Up

Jack 可补充财务表、访谈纪要/签到、交付台账、云效统计、缺陷/事故台账后，再让 ChatGPT 按 task_id 逐项判断。
"""
    THREAD_RECORD.write_text(content, encoding="utf-8")


def main() -> None:
    ensure_dirs()
    workbook_rows = extract_workbook()
    write_csv(
        OUT / "source-workbook-raw-extract.csv",
        workbook_rows,
        ["row"] + [f"col_{i}" for i in range(1, 12)],
    )
    write_csv(
        OUT / "performance-q1q2-task-list.csv",
        TASKS,
        ["task_id", "quarter", "indicator", "target", "scoring", "evidence_needed", "judgement_hint"],
    )
    shutil.copy2(OUT / "performance-q1q2-task-list.csv", UPLOAD / "performance-q1q2-task-list.csv")
    manifest = copy_selected_sources()
    write_csv(
        OUT / "evidence-manifest.csv",
        manifest,
        ["evidence_id", "title", "evidence_level", "source_path", "copied_to_upload_pack", "upload_relative_path", "note"],
    )
    manifest_by_id = {row["evidence_id"]: row for row in manifest}
    evidence_rows = build_task_evidence_rows(manifest_by_id)
    write_csv(
        OUT / "task-evidence-map.csv",
        evidence_rows,
        [
            "task_id",
            "quarter",
            "indicator",
            "target",
            "evidence_id",
            "evidence_title",
            "evidence_level",
            "source_path",
            "copied_to_upload_pack",
            "why_relevant",
            "snippet",
            "gap_or_caution",
        ],
    )
    for name in ["evidence-manifest.csv", "task-evidence-map.csv", "source-workbook-raw-extract.csv"]:
        shutil.copy2(OUT / name, UPLOAD / name)
    md = build_markdown(TASKS, evidence_rows, manifest)
    (OUT / "performance-q1q2-evidence-pack.md").write_text(md, encoding="utf-8")
    (UPLOAD / "README_姜阳绩效Q1Q2达标评估素材包.md").write_text(md, encoding="utf-8")
    build_html(md, OUT / "performance-q1q2-evidence-pack.html")
    shutil.copy2(OUT / "performance-q1q2-evidence-pack.html", UPLOAD / "performance-q1q2-evidence-pack.html")
    write_task_index()
    write_thread_record()
    zip_target = OUT / "jiangyang-performance-q1q2-chatgpt-pack-20260628.zip"
    zip_dir(UPLOAD, zip_target)
    DESKTOP.mkdir(parents=True, exist_ok=True)
    shutil.copy2(OUT / "performance-q1q2-evidence-pack.md", DESKTOP_FINAL / "performance-q1q2-evidence-pack.md")
    shutil.copy2(OUT / "performance-q1q2-evidence-pack.html", DESKTOP_FINAL / "performance-q1q2-evidence-pack.html")
    shutil.copy2(OUT / "task-evidence-map.csv", DESKTOP_FINAL / "task-evidence-map.csv")
    shutil.copy2(OUT / "performance-q1q2-task-list.csv", DESKTOP_FINAL / "performance-q1q2-task-list.csv")
    shutil.copy2(zip_target, DESKTOP_UPLOAD / zip_target.name)
    print(
        json.dumps(
            {
                "out": str(OUT),
                "desktop": str(DESKTOP),
                "zip": str(zip_target),
                "task_count": len(TASKS),
                "evidence_count": len(manifest),
                "mapped_rows": len(evidence_rows),
            },
            ensure_ascii=False,
            indent=2,
        )
    )


if __name__ == "__main__":
    main()
