from __future__ import annotations

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

from openpyxl import load_workbook


ROOT = Path(__file__).resolve().parents[1]
MAIN_WORKBOOK = ROOT / "测试用例" / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx"
FLOW_WORKBOOK = ROOT / "测试用例" / "订单状态流转_链路流程线整理.xlsx"
BACKUP_DIR = ROOT / "runtime" / "excel_backups"


CASE_RELATIONS = {
    "S-FLOW-072": (
        "承接打手代练中丸友集申请上游撤销后的终态结果，主要接在 S-FLOW-033 -> S-FLOW-076/077 后；后续分支为 S-FLOW-073/S-FLOW-074。",
        "2026-06-29 修正：072 不接在已发打手但未接单的 FLOW-012/FLOW-046 后；必须放在打手代练中丸友集撤销主单后。",
    ),
    "S-FLOW-073": ("承接 S-FLOW-072；被 FLOW-013 引用。", "追缴单需要打手继续完成追缴内容。"),
    "S-FLOW-074": ("承接 S-FLOW-072；被 FLOW-014 引用。", "处罚单前半段复用处罚追缴入口，结果以打手我的处罚查看处罚金额为准。"),
    "S-FLOW-075": ("承接 S-FLOW-021；后续分支为 S-FLOW-076（丸掌柜同意撤销）或 S-FLOW-077（丸掌柜拒绝撤销）。", "打手未接单阶段的上游撤销分支，不直接承接处罚追缴。"),
    "S-FLOW-076": ("承接 S-FLOW-075 或 S-FLOW-033；未接单撤销同意后接 S-FLOW-012，代练中撤销主单同意后可接 S-FLOW-072。", "用户确认：打手未接单时同意撤销后丸友集侧子单为已取消，不可接处罚追缴；打手代练中撤销主单后才进入处罚追缴分支。"),
    "S-FLOW-077": ("承接 S-FLOW-075 或 S-FLOW-033；未接单撤销拒绝后不接 S-FLOW-072，代练中撤销主单拒绝后可接 S-FLOW-072。", "FLOW-046 不再接处罚追缴；FLOW-014 使用打手代练中撤销主单后的拒绝结果接处罚追缴。"),
    "S-FLOW-033": ("承接 S-FLOW-026；后续由 S-FLOW-076 或 S-FLOW-077 处理丸掌柜同意/拒绝撤销。", "打手代练中时丸友集撤销主单，是 FLOW-013/014 的分支起点。"),
}


FLOW_UPDATES = {
    "FLOW-012": [
        "FLOW-012",
        "已发打手待接单-丸友集申请撤销主单-丸掌柜同意撤销-继续发单",
        "打手未接单撤销同意后续发",
        "已发打手；打手未接单；丸友集已申请撤销",
        "旧子单已取消；新子单待接单/已推单",
        "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-075 -> S-FLOW-076 -> S-FLOW-012",
        "S-FLOW-007(创建主单已推单) -> S-FLOW-014(丸友集确认接单) -> S-FLOW-021(丸友集立即发单到打手) -> S-FLOW-075(丸友集申请撤销此单) -> S-FLOW-076(丸掌柜同意撤销，丸友集子单已取消) -> S-FLOW-012(丸掌柜继续发单)",
        "上游发单 -> 丸友集申请撤销 -> 丸掌柜处理撤销 -> 上游继续发单",
        "验证打手未接单阶段丸掌柜同意撤销后，不能处罚追缴，只能由丸掌柜继续发单。",
        "可复用 S-FLOW-076 的订单继续执行 S-FLOW-012。",
        "2026-06-29 按实测修正：同意撤销后丸友集子单为已取消，不接 S-FLOW-072。",
    ],
    "FLOW-013": [
        "FLOW-013",
        "打手代练中丸友集撤销主单-同意撤销后处罚追缴追缴单流程",
        "代练中撤销主单同意 + 处罚追缴-追缴单",
        "已执行 S-FLOW-033；丸掌柜同意丸友集在打手代练中发起的撤销主单，丸友集侧子单为撤销完成",
        "追缴单由打手完成追缴",
        "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-026 -> S-FLOW-033 -> S-FLOW-076 -> S-FLOW-072 -> S-FLOW-073",
        "S-FLOW-007(创建主单已推单) -> S-FLOW-014(丸友集确认接单) -> S-FLOW-021(丸友集立即发单到打手) -> S-FLOW-026(打手立即抢单进入代练中) -> S-FLOW-033(丸友集在代练中撤销主单) -> S-FLOW-076(丸掌柜同意撤销，丸友集子单撤销完成) -> S-FLOW-072(打开处罚追缴并选择处理类型) -> S-FLOW-073(选择追缴单并由打手完成追缴)",
        "打手代练中 -> 工作室撤销主单 -> 丸掌柜同意撤销 -> 处罚追缴-追缴流程",
        "验证打手代练中撤销主单同意后，撤销完成子单进入处罚追缴，追缴单由打手完成。",
        "FLOW-012/FLOW-046 不再承接处罚追缴；追缴单和处罚单分支建议使用不同订单。",
        "2026-06-29 修正：FLOW-013 放到打手代练中撤销主单之后。",
    ],
    "FLOW-046": [
        "FLOW-046",
        "已发打手待接单-丸友集申请撤销主单-丸掌柜拒绝撤销",
        "打手未接单撤销拒绝",
        "已发打手；打手未接单；丸友集已申请撤销",
        "撤销被拒绝；不进入处罚追缴",
        "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-075 -> S-FLOW-077",
        "S-FLOW-007(创建主单已推单) -> S-FLOW-014(丸友集确认接单) -> S-FLOW-021(丸友集立即发单到打手) -> S-FLOW-075(丸友集申请撤销此单) -> S-FLOW-077(丸掌柜拒绝撤销)",
        "上游发单 -> 丸友集申请撤销 -> 丸掌柜拒绝撤销",
        "验证打手未接单阶段丸掌柜拒绝撤销后，不进入处罚追缴分支。",
        "后续按页面实际状态另行选择继续履约或其他处理，不把该分支作为 072 前置。",
        "2026-06-29 按用户确认修正：FLOW-046 后面不再接处罚追缴。",
    ],
    "FLOW-014": [
        "FLOW-014",
        "打手代练中丸友集撤销主单-拒绝撤销后处罚追缴处罚单流程",
        "代练中撤销主单拒绝 + 处罚追缴-处罚单",
        "已执行 S-FLOW-033；丸掌柜拒绝丸友集在打手代练中发起的撤销主单，丸友集侧子单为已完成",
        "处罚单生成并在打手侧可查看处罚金额",
        "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-026 -> S-FLOW-033 -> S-FLOW-077 -> S-FLOW-072 -> S-FLOW-074",
        "S-FLOW-007(创建主单已推单) -> S-FLOW-014(丸友集确认接单) -> S-FLOW-021(丸友集立即发单到打手) -> S-FLOW-026(打手立即抢单进入代练中) -> S-FLOW-033(丸友集在代练中撤销主单) -> S-FLOW-077(丸掌柜拒绝撤销，丸友集子单已完成) -> S-FLOW-072(打开处罚追缴并选择处理类型) -> S-FLOW-074(选择处罚单并在打手侧查看处罚金额)",
        "打手代练中 -> 工作室撤销主单 -> 丸掌柜拒绝撤销 -> 处罚追缴-处罚流程",
        "验证打手代练中撤销主单拒绝后，已完成子单进入处罚追缴，处罚单金额可在打手我的处罚中查看。",
        "处罚单分支不要求打手继续完成追缴任务。",
        "2026-06-29 修正：FLOW-014 放到打手代练中撤销主单之后。",
    ],
}


FLOW_CASES = {
    "FLOW-012": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-075", "S-FLOW-076", "S-FLOW-012"],
    "FLOW-046": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-075", "S-FLOW-077"],
    "FLOW-013": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-033", "S-FLOW-076", "S-FLOW-072", "S-FLOW-073"],
    "FLOW-014": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-033", "S-FLOW-077", "S-FLOW-072", "S-FLOW-074"],
}


def backup(path: Path, suffix: str) -> None:
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    target = BACKUP_DIR / f"{path.stem}.{suffix}.{datetime.now():%Y%m%d-%H%M%S}{path.suffix}"
    shutil.copy2(path, target)
    print(f"backup {target}")


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


def copy_style(ws, source: int, target: int) -> None:
    for column in range(1, ws.max_column + 1):
        src = ws.cell(source, column)
        dst = ws.cell(target, column)
        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 write_row(ws, row: int, values: list[object]) -> None:
    for column, value in enumerate(values, start=1):
        ws.cell(row, column).value = value
        ws.cell(row, column).alignment = copy(ws.cell(row, column).alignment)
        ws.cell(row, column).alignment = ws.cell(row, column).alignment.copy(wrap_text=True, vertical="top")


def upsert(ws, key: str, values: list[object]) -> None:
    row = find_row(ws, key)
    if row:
        write_row(ws, row, values)
    else:
        ws.append(values)
        copy_style(ws, max(2, ws.max_row - 1), ws.max_row)


def raw_meta(raw_ws, case_id: str) -> tuple[str, str, str, str]:
    for row in range(2, raw_ws.max_row + 1):
        if str(raw_ws.cell(row, 1).value or "").strip() == case_id:
            return (
                str(raw_ws.cell(row, 2).value or ""),
                str(raw_ws.cell(row, 3).value or ""),
                str(raw_ws.cell(row, 4).value or ""),
                str(raw_ws.cell(row, 5).value or ""),
            )
    return "", "", "", ""


def update_main_workbook() -> None:
    backup(MAIN_WORKBOOK, "before_fix_penalty_after_player_accept")
    wb = load_workbook(MAIN_WORKBOOK)
    ws = wb["订单状态流转"]
    headers = {str(ws.cell(1, col).value): col for col in range(1, ws.max_column + 1)}
    relation_col = headers.get("可组合/被引用关系")
    remark_col = headers.get("备注")
    assert relation_col and remark_col
    for case_id, (relation, remark) in CASE_RELATIONS.items():
        row = find_row(ws, case_id)
        if not row:
            continue
        ws.cell(row, relation_col).value = relation
        ws.cell(row, remark_col).value = remark
    wb.save(MAIN_WORKBOOK)
    print(f"updated {MAIN_WORKBOOK}")


def update_flow_workbook() -> None:
    backup(FLOW_WORKBOOK, "before_fix_penalty_after_player_accept")
    wb = load_workbook(FLOW_WORKBOOK)
    summary = wb["链路流程线"]
    details = wb["链路步骤明细"]
    coverage = wb["用例覆盖索引"]
    raw = wb["原始用例索引"]
    notes = wb["说明"]

    for flow_id, values in FLOW_UPDATES.items():
        upsert(summary, flow_id, values)

    for row in range(details.max_row, 1, -1):
        if str(details.cell(row, 1).value or "").strip() in FLOW_CASES:
            details.delete_rows(row)
    for flow_id, case_ids in FLOW_CASES.items():
        for index, case_id in enumerate(case_ids, start=1):
            module, title, precondition, expected = raw_meta(raw, case_id)
            details.append([flow_id, index, case_id, module, title, precondition, expected])
            copy_style(details, max(2, details.max_row - 1), details.max_row)

    flow_links: dict[str, list[str]] = {}
    for row in range(2, summary.max_row + 1):
        flow_id = str(summary.cell(row, 1).value or "").strip()
        sequence = str(summary.cell(row, 6).value or "")
        for case_id in [part.strip() for part in sequence.split("->") if part.strip()]:
            flow_links.setdefault(case_id, []).append(flow_id)
    for row in range(2, coverage.max_row + 1):
        case_id = str(coverage.cell(row, 1).value or "").strip()
        if not case_id.startswith("S-FLOW-"):
            continue
        links = flow_links.get(case_id, [])
        coverage.cell(row, 4).value = len(links)
        coverage.cell(row, 5).value = "、".join(links)
        coverage.cell(row, 6).value = "是" if links else "否"
        if case_id in CASE_RELATIONS:
            coverage.cell(row, 7).value = CASE_RELATIONS[case_id][1]

    notes.append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} 修正处罚追缴入口：S-FLOW-072 接在打手代练中丸友集撤销主单后的 S-FLOW-076/077 之后；FLOW-012/FLOW-046 不再接处罚追缴。"])
    copy_style(notes, max(2, notes.max_row - 1), notes.max_row)
    wb.save(FLOW_WORKBOOK)
    print(f"updated {FLOW_WORKBOOK}")


def main() -> None:
    update_main_workbook()
    update_flow_workbook()


if __name__ == "__main__":
    main()
