from __future__ import annotations

from copy import copy
from datetime import datetime
from pathlib import Path

from openpyxl import load_workbook


FLOW_XLSX = Path("测试用例/订单状态流转_链路流程线整理.xlsx")

FLOW_ROWS = {
    "FLOW-013": {
        "链路编号": "FLOW-013",
        "链路名称": "打手代练中子单撤销完成后处罚追缴追缴单流程",
        "链路类型": "代练中子单撤销完成 + 处罚追缴-追缴单",
        "起点状态": "已执行 S-FLOW-026；丸友集订单列表目标子单为代练中，可由丸友集账号发起子单撤销。",
        "终点状态": "追缴单由打手完成追缴后，丸友集继续发下一子单",
        "用例编号序列": "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-026 -> S-FLOW-078 -> S-FLOW-072 -> S-FLOW-073 -> S-FLOW-032",
        "用例链路（编号+标题）": (
            "S-FLOW-007(创建主单已推单) -> S-FLOW-014(丸友集确认接单) -> "
            "S-FLOW-021(丸友集立即发单到打手) -> S-FLOW-026(打手立即抢单进入代练中) -> "
            "S-FLOW-078(丸友集订单列表代练中子单-撤销子单并进入撤销完成) -> "
            "S-FLOW-072(打开处罚追缴并选择处理类型) -> "
            "S-FLOW-073(选择追缴单并由打手完成追缴) -> "
            "S-FLOW-032(前一子单已撤销/已取消状态-续发下一子单)"
        ),
        "涉及模块": "打手代练中 -> 丸友集撤销子单 -> 处罚追缴-追缴流程",
        "适用场景": "验证代练中子单由丸友集撤销完成后，撤销完成子单进入处罚追缴，追缴单由打手完成。",
        "数据复用建议": "FLOW-013/FLOW-014 建议使用不同订单；S-FLOW-078 产出的撤销完成子单可先衔接 S-FLOW-072，处罚追缴结束后继续衔接 S-FLOW-032。",
        "注意事项/缺口": "2026-06-30 修正：代练中子单不支持主单申请撤销，FLOW-013 改为子单撤销完成后进入处罚追缴，追缴完成后仍可继续发单。",
    },
    "FLOW-014": {
        "链路编号": "FLOW-014",
        "链路名称": "打手代练中子单撤销完成后处罚追缴处罚单流程",
        "链路类型": "代练中子单撤销完成 + 处罚追缴-处罚单",
        "起点状态": "已执行 S-FLOW-026；丸友集订单列表目标子单为代练中，可由丸友集账号发起子单撤销。",
        "终点状态": "处罚单生成并在打手侧可查看处罚金额后，丸友集继续发下一子单",
        "用例编号序列": "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-026 -> S-FLOW-078 -> S-FLOW-072 -> S-FLOW-074 -> S-FLOW-032",
        "用例链路（编号+标题）": (
            "S-FLOW-007(创建主单已推单) -> S-FLOW-014(丸友集确认接单) -> "
            "S-FLOW-021(丸友集立即发单到打手) -> S-FLOW-026(打手立即抢单进入代练中) -> "
            "S-FLOW-078(丸友集订单列表代练中子单-撤销子单并进入撤销完成) -> "
            "S-FLOW-072(打开处罚追缴并选择处理类型) -> "
            "S-FLOW-074(选择处罚单并在打手侧查看处罚金额) -> "
            "S-FLOW-032(前一子单已撤销/已取消状态-续发下一子单)"
        ),
        "涉及模块": "打手代练中 -> 丸友集撤销子单 -> 处罚追缴-处罚流程",
        "适用场景": "验证代练中子单由丸友集撤销完成后，撤销完成子单进入处罚追缴，处罚单金额可在打手我的处罚中查看。",
        "数据复用建议": "处罚单分支不要求打手继续完成追缴任务；FLOW-013/FLOW-014 建议使用不同订单；处罚单校验完成后继续衔接 S-FLOW-032。",
        "注意事项/缺口": "2026-06-30 修正：代练中子单不支持主单申请撤销，FLOW-014 改为子单撤销完成后进入处罚追缴，处罚确认后仍可继续发单。",
    },
}

STEP_SOURCE = {
    "S-FLOW-007": None,
    "S-FLOW-014": None,
    "S-FLOW-021": None,
    "S-FLOW-026": None,
    "S-FLOW-078": None,
    "S-FLOW-072": None,
    "S-FLOW-073": None,
    "S-FLOW-074": None,
    "S-FLOW-032": None,
}

FLOW_STEPS = {
    "FLOW-013": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-078", "S-FLOW-072", "S-FLOW-073", "S-FLOW-032"],
    "FLOW-014": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-078", "S-FLOW-072", "S-FLOW-074", "S-FLOW-032"],
}

COVERAGE_UPDATES = {
    "S-FLOW-033": {"出现链路": "FLOW-036、FLOW-037、FLOW-038、FLOW-020、FLOW-019", "覆盖次数": 5, "备注": "FLOW-013/014 已改为 S-FLOW-078 子单撤销完成后进入处罚追缴，不再引用主单申请撤销。"},
    "S-FLOW-076": {"出现链路": "FLOW-012", "覆盖次数": 1, "备注": "FLOW-013 已改为 S-FLOW-078 子单撤销完成后进入处罚追缴，不再引用 S-FLOW-076。"},
    "S-FLOW-077": {"出现链路": "FLOW-046", "覆盖次数": 1, "备注": "FLOW-014 已改为 S-FLOW-078 子单撤销完成后进入处罚追缴，不再引用 S-FLOW-077。"},
    "S-FLOW-078": {"出现链路": "FLOW-018、FLOW-013、FLOW-014", "覆盖次数": 3, "备注": "承接 S-FLOW-026 的代练中子单；撤销完成后可接 S-FLOW-032 或 S-FLOW-072。"},
    "S-FLOW-072": {"出现链路": "FLOW-013、FLOW-014", "覆盖次数": 2, "备注": "承接 S-FLOW-078 产出的撤销完成子单，后接追缴单/处罚单分支。"},
    "S-FLOW-032": {"出现链路": "FLOW-018、FLOW-022、FLOW-046、FLOW-013、FLOW-014", "覆盖次数": 5, "备注": "FLOW-013/014 处罚追缴分支结束后，仍可基于撤销完成子单继续发下一子单。"},
}


def headers(ws) -> dict[str, int]:
    return {str(cell.value).strip(): cell.column for cell in ws[1] if cell.value}


def find_row(ws, value: str, column: int = 1) -> int | None:
    for row in range(2, ws.max_row + 1):
        if str(ws.cell(row, column).value or "").strip() == value:
            return row
    return None


def copy_row_style(ws, source_row: int, target_row: int) -> None:
    for col in range(1, ws.max_column + 1):
        src = ws.cell(source_row, col)
        dst = ws.cell(target_row, col)
        if src.has_style:
            dst._style = copy(src._style)
        dst.font = copy(src.font)
        dst.fill = copy(src.fill)
        dst.border = copy(src.border)
        dst.alignment = copy(src.alignment)
        dst.number_format = src.number_format


def update_summary(ws) -> None:
    h = headers(ws)
    for flow_id, values in FLOW_ROWS.items():
        row = find_row(ws, flow_id)
        if row is None:
            row = ws.max_row + 1
            copy_row_style(ws, row - 1, row)
        for name, value in values.items():
            col = h.get(name)
            if col:
                ws.cell(row, col).value = value


def load_step_sources(ws) -> None:
    h = headers(ws)
    for row in range(2, ws.max_row + 1):
        case_id = str(ws.cell(row, h["用例编号"]).value or "").strip()
        if case_id in STEP_SOURCE and STEP_SOURCE[case_id] is None:
            STEP_SOURCE[case_id] = {
                "所属模块": ws.cell(row, h["所属模块"]).value,
                "用例标题": ws.cell(row, h["用例标题"]).value,
                "前置条件": ws.cell(row, h["前置条件"]).value,
                "预期结果": ws.cell(row, h["预期结果"]).value,
            }
    missing = [case_id for case_id, values in STEP_SOURCE.items() if values is None]
    if missing:
        raise RuntimeError(f"链路步骤明细缺少用例来源行：{missing}")


def delete_flow_steps(ws, flow_ids: set[str]) -> int:
    first = None
    row = 2
    while row <= ws.max_row:
        if str(ws.cell(row, 1).value or "").strip() in flow_ids:
            if first is None:
                first = row
            ws.delete_rows(row, 1)
            continue
        row += 1
    return first or ws.max_row + 1


def update_details(ws) -> None:
    load_step_sources(ws)
    insert_at = delete_flow_steps(ws, {"FLOW-013", "FLOW-014"})
    rows = []
    for flow_id, case_ids in FLOW_STEPS.items():
        for index, case_id in enumerate(case_ids, start=1):
            source = STEP_SOURCE[case_id]
            rows.append([
                flow_id,
                index,
                case_id,
                source["所属模块"],
                source["用例标题"],
                source["前置条件"],
                source["预期结果"],
            ])
    ws.insert_rows(insert_at, len(rows))
    style_source = min(insert_at + len(rows), ws.max_row)
    for offset in range(len(rows)):
        copy_row_style(ws, style_source, insert_at + offset)
        for col, value in enumerate(rows[offset], start=1):
            ws.cell(insert_at + offset, col).value = value


def update_coverage(ws) -> None:
    h = headers(ws)
    for case_id, values in COVERAGE_UPDATES.items():
        row = find_row(ws, case_id)
        if row is None:
            continue
        for name, value in values.items():
            col = h.get(name)
            if col:
                ws.cell(row, col).value = value


def main() -> None:
    wb = load_workbook(FLOW_XLSX)
    update_summary(wb["链路流程线"])
    update_details(wb["链路步骤明细"])
    update_coverage(wb["用例覆盖索引"])
    wb["说明"].append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} FLOW-013/014 处罚追缴分支末尾追加 S-FLOW-032 继续发单。"])
    wb.save(FLOW_XLSX)
    print(f"updated {FLOW_XLSX}")


if __name__ == "__main__":
    main()
