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"


def steps(items: list[str]) -> str:
    return "\n".join(f"{index}. {item}" for index, item in enumerate(items, start=1))


def status_row(
    case_id: str,
    module: str,
    title: str,
    precondition: str,
    action_steps: list[str],
    expected: str,
    assertion: str,
    relation: str,
    status: str = "待自动化",
    remark: str = "",
) -> list[str]:
    return [
        case_id,
        "订单状态流转用例",
        module,
        title,
        "丸友集：工作室主账号；丸掌柜：企业主账号；打手应用：打手账号。",
        precondition,
        "主单号、来源单号、子单号、订单标题、打手账号、撤销原因、撤销凭证。",
        steps(action_steps),
        expected,
        assertion,
        relation,
        status,
        remark,
    ]


CASES = {
    "S-FLOW-022": status_row(
        "S-FLOW-022",
        "下游发起撤销",
        "【不适用】已发打手待接单阶段-丸友集撤回发单不可用",
        "已执行 S-FLOW-021，丸友集已将订单发到打手抢单大厅，打手尚未接单；按当前平台规则此阶段不能走“撤回发单/重新发单”。",
        [
            "登录丸友集工作室主账号。",
            "进入工作室履约应用-订单列表。",
            "搜索已执行 S-FLOW-021 的目标订单。",
            "打开订单详情或子单列表，确认打手子单已发出且打手未接单。",
            "检查操作区是否存在【撤回发单】或可直接重新发单入口。",
            "记录页面实际可用操作；不得继续尝试按撤回发单推进链路。",
        ],
        "页面不支持已发打手未接单阶段直接撤回发单并重新发单；FLOW-012 不再承接本用例。",
        "S-FLOW-021 后不能出现 S-FLOW-022 -> S-FLOW-021 的链路；后续应改走 S-FLOW-075 丸友集申请撤销，并由丸掌柜同意/拒绝处理。",
        "已废弃/不再被 FLOW-012 引用。",
        status="不适用",
        remark="2026-06-29 用户确认：S-FLOW-021 后不能撤回发单，原 S-FLOW-022 撤回发单链路作废。",
    ),
    "S-FLOW-075": status_row(
        "S-FLOW-075",
        "丸友集申请撤销",
        "丸友集已发单到打手抢单大厅/打手未接单阶段-丸友集申请撤销此单",
        "已执行 S-FLOW-021，丸友集已将订单发到打手抢单大厅，打手尚未接单；页面不能撤回发单，只能由丸友集发起撤销申请，等待丸掌柜处理。",
        [
            "登录丸友集工作室主账号。",
            "进入丸友集-工作室履约应用-订单列表。",
            "按主单号、来源单号或订单标题搜索目标订单。",
            "打开订单详情或子单列表，确认打手子单已发出且打手未接单。",
            "点击【申请撤销】、【撤销订单】、【向上撤销】或页面等价撤销入口。",
            "填写撤销原因，并按页面要求上传撤销凭证。",
            "提交撤销申请。",
            "登录丸掌柜主账号，进入商家管理-订单管理。",
            "搜索同一订单，进入订单详情，确认可看到丸友集撤销申请及处理撤销入口。",
        ],
        "丸友集撤销申请提交成功；丸掌柜侧订单详情出现待处理撤销申请/处理撤销入口。",
        "丸友集侧订单进入撤销中/待上游处理等页面实际待处理状态；丸掌柜侧主单号、来源单号、子单号、撤销原因与丸友集侧一致。",
        "承接 S-FLOW-021；后续分支为 S-FLOW-076（丸掌柜同意撤销）或 S-FLOW-077（丸掌柜拒绝撤销）。",
        remark="替代原错误的 S-FLOW-022 撤回发单动作。",
    ),
    "S-FLOW-076": status_row(
        "S-FLOW-076",
        "丸掌柜处理撤销",
        "【丸掌柜】同意丸友集已发打手未接单订单撤销-丸友集子单撤销完成",
        "已执行 S-FLOW-075，丸友集已提交撤销申请；丸掌柜订单详情存在待处理撤销申请。",
        [
            "登录丸掌柜主账号。",
            "进入丸掌柜-商家管理-订单管理。",
            "按主单号、来源单号或订单标题搜索目标订单。",
            "进入订单详情，定位丸友集撤销申请或子单列表中的撤销中记录。",
            "点击【处理撤销】、【撤销审核】或页面等价处理入口。",
            "选择【同意撤销】。",
            "填写处理备注并确认提交。",
            "登录丸友集工作室主账号，进入订单列表搜索同一订单。",
            "打开订单详情或子单列表，回查目标子单状态。",
        ],
        "丸掌柜同意撤销成功；丸友集侧目标子单状态变为撤销完成；该终态子单可继续衔接 S-FLOW-072。",
        "丸掌柜处理结果、丸友集子单撤销完成状态、主单号/来源单号/子单号一致；后续可对撤销完成子单执行处罚追缴。",
        "承接 S-FLOW-075；被 FLOW-012 引用；后续可接 S-FLOW-072。",
        remark="用户确认：同意撤销后丸友集侧子单状态为撤销完成。",
    ),
    "S-FLOW-077": status_row(
        "S-FLOW-077",
        "丸掌柜处理撤销",
        "【丸掌柜】拒绝丸友集已发打手未接单订单撤销-丸友集子单已完成",
        "已执行 S-FLOW-075，丸友集已提交撤销申请；丸掌柜订单详情存在待处理撤销申请。",
        [
            "登录丸掌柜主账号。",
            "进入丸掌柜-商家管理-订单管理。",
            "按主单号、来源单号或订单标题搜索目标订单。",
            "进入订单详情，定位丸友集撤销申请或子单列表中的撤销中记录。",
            "点击【处理撤销】、【撤销审核】或页面等价处理入口。",
            "选择【拒绝撤销】或【驳回撤销】。",
            "填写拒绝原因/处理备注并确认提交。",
            "登录丸友集工作室主账号，进入订单列表搜索同一订单。",
            "打开订单详情或子单列表，回查目标子单状态。",
        ],
        "丸掌柜拒绝撤销成功；丸友集侧目标子单状态变为已完成；该终态子单可继续衔接 S-FLOW-072。",
        "丸掌柜拒绝处理结果、丸友集子单已完成状态、主单号/来源单号/子单号一致；后续可对已完成子单执行处罚追缴。",
        "承接 S-FLOW-075；被 FLOW-046 引用；后续可接 S-FLOW-072。",
        remark="用户确认：拒绝撤销后丸友集侧子单状态都会变成已完成。",
    ),
}


FLOWS = {
    "FLOW-012": [
        "FLOW-012",
        "已发打手待接单-丸友集申请撤销-丸掌柜同意撤销-处罚追缴",
        "已发打手未接单撤销同意 + 处罚追缴",
        "已执行 S-FLOW-021；不能撤回发单，只能由丸友集申请撤销",
        "丸掌柜同意撤销；丸友集侧子单撤销完成；可进入处罚追缴",
        "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-075 -> S-FLOW-076 -> S-FLOW-072",
        "S-FLOW-007(【丸掌柜】主单待接单状态/子单待接单-已推单状态-创建主单已推单) -> S-FLOW-014(丸友集抢单大厅待接单阶段-丸友集确认接单) -> S-FLOW-021(丸友集已接单/未发单到打手抢单大厅阶段-立即发单) -> S-FLOW-075(丸友集已发单到打手抢单大厅/打手未接单阶段-丸友集申请撤销此单) -> S-FLOW-076(【丸掌柜】同意丸友集已发打手未接单订单撤销-丸友集子单撤销完成) -> S-FLOW-072(【丸友集】已完成/撤销完成子单-发起追缴单)",
        "上游发单 -> 丸友集申请撤销 -> 丸掌柜处理撤销 -> 处罚追缴-丸友集发起",
        "验证丸友集发给打手后、打手未接单前不可撤回发单，只能发起撤销；丸掌柜同意后丸友集侧子单为撤销完成，并可继续处罚追缴。",
        "同意撤销后的终态子单可作为 S-FLOW-072 的撤销完成前置数据。",
        "2026-06-29 修正：移除错误的 S-FLOW-022 撤回发单和 S-FLOW-021 重新发单衔接。",
    ],
    "FLOW-046": [
        "FLOW-046",
        "已发打手待接单-丸友集申请撤销-丸掌柜拒绝撤销-处罚追缴",
        "已发打手未接单撤销拒绝 + 处罚追缴",
        "已执行 S-FLOW-021；不能撤回发单，只能由丸友集申请撤销",
        "丸掌柜拒绝撤销；丸友集侧子单已完成；可进入处罚追缴",
        "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-075 -> S-FLOW-077 -> S-FLOW-072",
        "S-FLOW-007(【丸掌柜】主单待接单状态/子单待接单-已推单状态-创建主单已推单) -> S-FLOW-014(丸友集抢单大厅待接单阶段-丸友集确认接单) -> S-FLOW-021(丸友集已接单/未发单到打手抢单大厅阶段-立即发单) -> S-FLOW-075(丸友集已发单到打手抢单大厅/打手未接单阶段-丸友集申请撤销此单) -> S-FLOW-077(【丸掌柜】拒绝丸友集已发打手未接单订单撤销-丸友集子单已完成) -> S-FLOW-072(【丸友集】已完成/撤销完成子单-发起追缴单)",
        "上游发单 -> 丸友集申请撤销 -> 丸掌柜处理撤销 -> 处罚追缴-丸友集发起",
        "验证丸友集发给打手后、打手未接单前不可撤回发单，只能发起撤销；丸掌柜拒绝后丸友集侧子单为已完成，并可继续处罚追缴。",
        "拒绝撤销后的终态子单可作为 S-FLOW-072 的已完成前置数据。",
        "2026-06-29 新增拒绝撤销分支，按用户确认拒绝后丸友集侧子单状态变为已完成。",
    ],
}


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


def copy_style(ws, src_row: int, dst_row: int) -> None:
    if src_row < 1 or src_row == dst_row:
        return
    for col in range(1, ws.max_column + 1):
        src = ws.cell(src_row, col)
        dst = ws.cell(dst_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 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 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], after: str | None = None) -> None:
    row = find_row(ws, key)
    if row:
        write_row(ws, row, values)
        return
    if after and (anchor := find_row(ws, after)):
        row = anchor + 1
        ws.insert_rows(row)
        copy_style(ws, anchor, row)
        write_row(ws, row, values)
        return
    ws.append(values)
    copy_style(ws, max(2, ws.max_row - 1), ws.max_row)


def delete_flow_steps(ws, flow_ids: set[str]) -> None:
    for row in range(ws.max_row, 1, -1):
        if str(ws.cell(row, 1).value or "").strip() in flow_ids:
            ws.delete_rows(row)


def update_main() -> None:
    backup(MAIN_WORKBOOK)
    workbook = load_workbook(MAIN_WORKBOOK)
    ws = workbook["订单状态流转"]
    upsert(ws, "S-FLOW-022", CASES["S-FLOW-022"])
    upsert(ws, "S-FLOW-075", CASES["S-FLOW-075"], "S-FLOW-074")
    upsert(ws, "S-FLOW-076", CASES["S-FLOW-076"], "S-FLOW-075")
    upsert(ws, "S-FLOW-077", CASES["S-FLOW-077"], "S-FLOW-076")
    workbook.save(MAIN_WORKBOOK)
    print(f"updated {MAIN_WORKBOOK}")


def raw_meta(raw_ws, case_id: str) -> tuple[str, str, str, str]:
    if case_id in CASES:
        row = CASES[case_id]
        return row[2], row[3], row[5], row[8]
    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_flow() -> None:
    backup(FLOW_WORKBOOK)
    workbook = load_workbook(FLOW_WORKBOOK)
    summary = workbook["链路流程线"]
    details = workbook["链路步骤明细"]
    coverage = workbook["用例覆盖索引"]
    raw = workbook["原始用例索引"]
    notes = workbook["说明"]

    for case_id in ["S-FLOW-022", "S-FLOW-075", "S-FLOW-076", "S-FLOW-077"]:
        row = CASES[case_id]
        upsert(raw, case_id, [case_id, row[2], row[3], row[5], row[8], row[7]], "S-FLOW-074")

    delete_flow_steps(summary, {"FLOW-044", "FLOW-045"})
    upsert(summary, "FLOW-012", FLOWS["FLOW-012"])
    upsert(summary, "FLOW-046", FLOWS["FLOW-046"], "FLOW-045")
    delete_flow_steps(details, {"FLOW-012", "FLOW-044", "FLOW-045", "FLOW-046"})
    for flow_id, case_ids in {
        "FLOW-012": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-075", "S-FLOW-076", "S-FLOW-072"],
        "FLOW-046": ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-075", "S-FLOW-077", "S-FLOW-072"],
    }.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)

    coverage_updates = {
        "S-FLOW-022": ("下游发起撤销", CASES["S-FLOW-022"][3], 0, "", "否", "不再被 FLOW-012 引用；原撤回发单链路作废。"),
        "S-FLOW-075": ("丸友集申请撤销", CASES["S-FLOW-075"][3], 2, "FLOW-012、FLOW-046", "是", "替代原 S-FLOW-022 的错误撤回发单动作。"),
        "S-FLOW-076": ("丸掌柜处理撤销", CASES["S-FLOW-076"][3], 1, "FLOW-012", "是", "同意撤销后接 S-FLOW-072。"),
        "S-FLOW-077": ("丸掌柜处理撤销", CASES["S-FLOW-077"][3], 1, "FLOW-046", "是", "拒绝撤销后接 S-FLOW-072。"),
        "S-FLOW-072": ("处罚追缴-丸友集发起", "【丸友集】已完成/撤销完成子单-发起追缴单", 2, "FLOW-012、FLOW-046", "是", "作为具体链路后的处罚追缴入口，不再单列 FLOW-044。"),
        "S-FLOW-073": ("处罚追缴-打手处理", "【打手】我的处罚-完成追缴单", 0, "", "否", "追缴单后续处理原子用例，后续生成脚本时由具体追缴链路引用，不单列 FLOW-044。"),
        "S-FLOW-074": ("处罚追缴-丸友集发起", "【丸友集】已完成/撤销完成子单-发起处罚单并在打手侧查看金额", 0, "", "否", "处罚单分支原子用例，后续生成脚本时由具体处罚链路引用，不单列 FLOW-045。"),
    }
    for case_id, values in coverage_updates.items():
        upsert(coverage, case_id, [case_id, *values], "S-FLOW-074")

    notes.append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} 二次修正 FLOW-012：S-FLOW-022 标记不适用，链路改接 S-FLOW-075/076/077 后进入 S-FLOW-072；删除独立 FLOW-044/FLOW-045。"])
    copy_style(notes, max(2, notes.max_row - 1), notes.max_row)
    workbook.save(FLOW_WORKBOOK)
    print(f"updated {FLOW_WORKBOOK}")


def main() -> None:
    update_main()
    update_flow()


if __name__ == "__main__":
    main()
