from __future__ import annotations

import json
import zipfile
from datetime import datetime
from pathlib import Path
from xml.sax.saxutils import escape


ROOT = Path(__file__).resolve().parents[1]
CASES_PATH = ROOT / "scripts" / "order_status_flow_cases.json"
OUTPUT = ROOT / "订单状态流转_组合流程查缺补漏.docx"


def load_cases() -> dict[str, dict[str, str]]:
    cases = json.loads(CASES_PATH.read_text(encoding="utf-8"))
    return {item["用例编号"]: item for item in cases}


def p(text: str = "", style: str | None = None) -> str:
    style_xml = f'<w:pStyle w:val="{style}"/>' if style else ""
    lines = text.split("\n") or [""]
    runs = []
    for index, line in enumerate(lines):
        if index:
            runs.append("<w:r><w:br/></w:r>")
        runs.append(f"<w:r><w:t>{escape(line)}</w:t></w:r>")
    return f"<w:p><w:pPr>{style_xml}</w:pPr>{''.join(runs)}</w:p>"


def bullet(text: str) -> str:
    return p("• " + text)


def heading(text: str, level: int = 1) -> str:
    return p(text, f"Heading{level}")


def case_ref(cases: dict[str, dict[str, str]], case_id: str) -> str:
    item = cases[case_id]
    return f"{case_id}（{item['用例标题']}）"


def flow_block(
    cases: dict[str, dict[str, str]],
    flow_id: str,
    title: str,
    refs: list[str],
    path: list[str],
    expected: list[str],
    gaps: list[str] | None = None,
) -> list[str]:
    body: list[str] = [heading(f"{flow_id} {title}", 3)]
    body.append(p("引用用例：" + " -> ".join(case_ref(cases, ref) for ref in refs)))
    body.append(p("组合路径："))
    body.extend(bullet(item) for item in path)
    body.append(p("组合预期："))
    body.extend(bullet(item) for item in expected)
    if gaps:
        body.append(p("查缺补漏："))
        body.extend(bullet(item) for item in gaps)
    return body


def build_document() -> str:
    cases = load_cases()
    parts: list[str] = []
    parts.append(heading("订单状态流转组合流程查缺补漏", 1))
    parts.append(p(f"生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"))
    parts.append(
        p(
            "说明：本文档基于 Excel【订单状态流转】sheet 当前 S-FLOW 用例组合生成，"
            "用于检查从“丸掌柜发单（主单待接单 / 子单待接单）”出发的组合流程是否完整。"
            "文档中的“查缺补漏”项表示当前 Excel 中需要进一步拆分或页面确认的候选分支。"
        )
    )

    parts.append(heading("一、状态节点索引", 2))
    for case_id in sorted(cases):
        item = cases[case_id]
        parts.append(bullet(f"{case_id}：{item['用例标题']}"))

    parts.append(heading("二、组合流程清单", 2))

    flows = [
        (
            "F-ORDER-001",
            "未推单待接单阶段-发起售后并处理售后",
            ["S-FLOW-001", "S-FLOW-006", "S-FLOW-007"],
            [
                "丸掌柜创建主单，主单待接单 / 子单待接单，尚未推送到丸友集。",
                "丸掌柜在该状态下发起售后。",
                "丸掌柜处理售后。",
            ],
            [
                "丸掌柜最终订单状态为已售后，子单状态为已售后。",
                "丸友集抢单大厅查询不到此单。",
                "该链路不涉及打手。",
            ],
            [],
        ),
        (
            "F-ORDER-002",
            "未推单待接单阶段-发起售后并取消售后",
            ["S-FLOW-001", "S-FLOW-006", "S-FLOW-008"],
            [
                "丸掌柜创建主单，主单待接单 / 子单待接单，尚未推送到丸友集。",
                "丸掌柜发起售后。",
                "丸掌柜取消售后。",
            ],
            [
                "丸掌柜订单状态恢复为待接单。",
                "丸掌柜子单状态恢复为待接单。",
                "丸友集仍查询不到此单。",
            ],
            [],
        ),
        (
            "F-ORDER-003",
            "已推单待接单阶段-发起售后并处理售后",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-009", "S-FLOW-010"],
            [
                "丸掌柜创建主单并推单，主单待接单 / 子单待接单-已推单。",
                "丸友集抢单大厅可见订单但尚未确认接单。",
                "丸掌柜发起售后。",
                "丸掌柜处理售后。",
            ],
            [
                "丸掌柜最终订单状态为已售后，子单状态为已售后。",
                "丸友集抢单大厅不再允许继续接单。",
                "该链路不涉及打手。",
            ],
            [],
        ),
        (
            "F-ORDER-004",
            "已推单待接单阶段-发起售后并取消售后",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-009", "S-FLOW-011"],
            [
                "丸掌柜创建主单并推单，主单待接单 / 子单待接单-已推单。",
                "丸掌柜发起售后。",
                "丸掌柜取消售后。",
            ],
            [
                "丸掌柜订单状态恢复为待接单。",
                "丸掌柜子单状态恢复为待接单-已推单。",
                "丸友集抢单大厅恢复到售后前的可见性或页面允许状态。",
            ],
            [],
        ),
        (
            "F-ORDER-005",
            "丸友集已接单未发打手阶段-售后组合缺口",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003"],
            [
                "丸掌柜创建主单并推单。",
                "丸友集在抢单大厅确认接单，订单进入丸友集订单列表。",
                "此时尚未立即发单给打手。",
            ],
            [
                "丸友集为已接单/承接状态。",
                "打手侧仍无订单。",
                "丸掌柜侧需要确认主单和子单的实际展示状态。",
            ],
            [
                "当前 S-FLOW 未单独拆出“丸友集已接单但未发打手阶段发起售后”。",
                "建议新增：主单待接单状态/子单待接单-已接单状态阶段发起售后。",
                "建议新增：主单待接单状态/子单待接单-已接单状态阶段处理售后。",
                "建议新增：主单待接单状态/子单待接单-已接单状态阶段取消售后。",
            ],
        ),
        (
            "F-ORDER-006",
            "已发打手待接单阶段-售后组合缺口",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004"],
            [
                "丸掌柜创建主单并推单。",
                "丸友集确认接单。",
                "丸友集立即发单给打手。",
                "打手侧为待接单，尚未接单。",
            ],
            [
                "打手待接单。",
                "丸友集可见已发出的打手子单。",
                "丸掌柜侧需要确认主单/子单实际展示状态。",
            ],
            [
                "当前 S-FLOW 未单独拆出“已发打手但打手未接单阶段发起售后”。",
                "建议新增：已发打手待接单阶段发起售后、处理售后、取消售后。",
            ],
        ),
        (
            "F-ORDER-007",
            "代练中阶段-发起售后并处理售后",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-012", "S-FLOW-013"],
            [
                "丸掌柜创建主单并推单。",
                "丸友集确认接单并发给打手。",
                "打手接单，打手订单进入代练中。",
                "丸掌柜在主单服务中 / 子单代练中阶段发起售后。",
                "丸掌柜处理售后。",
            ],
            [
                "丸掌柜最终订单状态为已售后，子单状态为已售后。",
                "丸友集进入售后完成。",
                "打手普通履约链路终止或受限。",
            ],
            [],
        ),
        (
            "F-ORDER-008",
            "代练中阶段-发起售后并取消售后",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-012", "S-FLOW-014"],
            [
                "丸掌柜创建主单并推单。",
                "丸友集确认接单并发给打手。",
                "打手接单，进入代练中。",
                "丸掌柜发起售后。",
                "丸掌柜取消售后。",
            ],
            [
                "丸掌柜订单状态恢复服务中，子单状态恢复代练中。",
                "丸友集和打手恢复普通履约状态。",
            ],
            [],
        ),
        (
            "F-ORDER-009",
            "打手撤销-丸友集同意撤销后二发",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-021", "S-FLOW-023", "S-FLOW-025"],
            [
                "打手接单进入代练中。",
                "打手发起撤销。",
                "丸友集同意撤销。",
                "丸友集对已撤销子单二发新子单。",
            ],
            [
                "原打手子单撤销完成。",
                "新打手子单待接单或代练中。",
                "丸掌柜不因原子单撤销直接完结。",
            ],
            [],
        ),
        (
            "F-ORDER-010",
            "打手撤销-丸友集拒绝撤销继续履约",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-021", "S-FLOW-024"],
            [
                "打手接单进入代练中。",
                "打手发起撤销。",
                "丸友集拒绝撤销。",
            ],
            [
                "打手订单恢复代练中。",
                "丸友集打手子单恢复代练中。",
                "丸掌柜订单状态保持服务中，子单状态保持代练中。",
            ],
            [],
        ),
        (
            "F-ORDER-011",
            "丸友集申请上游撤销-异常中售后处理",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-026", "S-FLOW-018", "S-FLOW-019"],
            [
                "打手进入代练中。",
                "丸友集申请上游撤销。",
                "丸掌柜进入异常中。",
                "丸掌柜在异常中阶段发起售后。",
                "丸掌柜处理售后。",
            ],
            [
                "异常中转售后中后，最终进入已售后。",
                "丸友集售后完成。",
            ],
            [],
        ),
        (
            "F-ORDER-012",
            "打手验收-丸友集申请上游验收-丸掌柜同意验收",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-034", "S-FLOW-036", "S-FLOW-038", "S-FLOW-039"],
            [
                "打手接单进入代练中。",
                "打手申请验收。",
                "丸友集同意验收子单。",
                "丸友集申请上游验收。",
                "丸掌柜同意验收。",
            ],
            [
                "打手订单已完成。",
                "丸友集父单已完成。",
                "丸掌柜订单状态已完成，子单状态已完成。",
            ],
            [],
        ),
        (
            "F-ORDER-013",
            "待验收阶段-发起售后并处理售后",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-034", "S-FLOW-036", "S-FLOW-038", "S-FLOW-015", "S-FLOW-016"],
            [
                "打手申请验收。",
                "丸友集同意验收子单。",
                "丸友集申请上游验收。",
                "丸掌柜进入待验收。",
                "丸掌柜发起售后。",
                "丸掌柜处理售后。",
            ],
            [
                "丸掌柜从待验收转售后中，再转已售后。",
                "丸友集同步售后完成。",
            ],
            [],
        ),
        (
            "F-ORDER-014",
            "多子单-前一子单完成后续发下一子单",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-034", "S-FLOW-036", "S-FLOW-032"],
            [
                "第一打手子单进入已完成。",
                "丸友集在页面允许时续发下一子单。",
            ],
            [
                "第一子单保持已完成。",
                "第二子单进入待接单或代练中。",
                "丸掌柜主单子单计数或子单列表更新。",
            ],
            [],
        ),
        (
            "F-ORDER-015",
            "多子单-前一子单撤销或取消后续发下一子单",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-021", "S-FLOW-023", "S-FLOW-033"],
            [
                "第一打手子单撤销或取消并审批通过。",
                "丸友集按页面规则续发下一子单。",
            ],
            [
                "第一子单保持已撤销或已取消。",
                "第二子单进入待接单或代练中。",
            ],
            [],
        ),
        (
            "F-ORDER-016",
            "已完成后申诉-确认赔偿",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-034", "S-FLOW-036", "S-FLOW-038", "S-FLOW-039", "S-FLOW-042", "S-FLOW-044"],
            [
                "丸掌柜同意验收，订单已完成。",
                "丸掌柜发起申诉。",
                "丸友集确认赔偿。",
            ],
            [
                "订单主状态保持已完成。",
                "申诉处理完成并生成赔偿记录。",
            ],
            [],
        ),
        (
            "F-ORDER-017",
            "已完成后申诉-驳回申诉",
            ["S-FLOW-001", "S-FLOW-002", "S-FLOW-003", "S-FLOW-004", "S-FLOW-005", "S-FLOW-034", "S-FLOW-036", "S-FLOW-038", "S-FLOW-039", "S-FLOW-042", "S-FLOW-045"],
            [
                "丸掌柜同意验收，订单已完成。",
                "丸掌柜发起申诉。",
                "丸友集驳回申诉。",
            ],
            [
                "订单主状态保持已完成。",
                "丸掌柜可见驳回原因。",
                "不生成赔偿金额。",
            ],
            [],
        ),
    ]

    for args in flows:
        parts.extend(flow_block(cases, *args))

    parts.append(heading("三、当前明显缺口汇总", 2))
    gaps = [
        "需要补充“丸友集已接单但未发打手”阶段的发起售后、处理售后、取消售后。",
        "需要补充“已发打手但打手未接单”阶段的发起售后、处理售后、取消售后。",
        "需要确认丸掌柜侧在“丸友集已接单/已发打手待接单”阶段显示的主单状态和子单状态命名。",
        "需要确认指派单取消、撤销审批后，丸掌柜是否展示异常中、待继续发单或其他聚合状态。",
        "需要后续结合页面实测补充售后处理的不同结果：退款、无退款、部分退款等资金分支。",
    ]
    parts.extend(bullet(item) for item in gaps)

    body = "".join(parts)
    return f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    {body}
    <w:sectPr>
      <w:pgSz w:w="11906" w:h="16838"/>
      <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
    </w:sectPr>
  </w:body>
</w:document>"""


def write_docx(document_xml: str) -> None:
    content_types = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  <Default Extension="xml" ContentType="application/xml"/>
  <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
  <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
</Types>"""
    rels = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>"""
    doc_rels = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>"""
    styles = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:style w:type="paragraph" w:default="1" w:styleId="Normal">
    <w:name w:val="Normal"/>
    <w:rPr><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="21"/></w:rPr>
  </w:style>
  <w:style w:type="paragraph" w:styleId="Heading1">
    <w:name w:val="heading 1"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/>
    <w:pPr><w:spacing w:before="240" w:after="120"/></w:pPr>
    <w:rPr><w:b/><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="32"/></w:rPr>
  </w:style>
  <w:style w:type="paragraph" w:styleId="Heading2">
    <w:name w:val="heading 2"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/>
    <w:pPr><w:spacing w:before="220" w:after="100"/></w:pPr>
    <w:rPr><w:b/><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="28"/></w:rPr>
  </w:style>
  <w:style w:type="paragraph" w:styleId="Heading3">
    <w:name w:val="heading 3"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/>
    <w:pPr><w:spacing w:before="180" w:after="80"/></w:pPr>
    <w:rPr><w:b/><w:rFonts w:ascii="Microsoft YaHei" w:eastAsia="Microsoft YaHei"/><w:sz w:val="24"/></w:rPr>
  </w:style>
</w:styles>"""
    with zipfile.ZipFile(OUTPUT, "w", compression=zipfile.ZIP_DEFLATED) as docx:
        docx.writestr("[Content_Types].xml", content_types)
        docx.writestr("_rels/.rels", rels)
        docx.writestr("word/_rels/document.xml.rels", doc_rels)
        docx.writestr("word/document.xml", document_xml)
        docx.writestr("word/styles.xml", styles)


def main() -> None:
    write_docx(build_document())
    print(OUTPUT)


if __name__ == "__main__":
    main()
