from __future__ import annotations

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

from openpyxl import load_workbook


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


CASES = {
    "S-FLOW-054": [
        "S-FLOW-054",
        "订单状态流转用例",
        "B-WZG-AFTER-ACCEPT-001：丸掌柜验收第1单子单后处罚第1单",
        "订单验收后处罚-上游发起",
        "【丸掌柜】子单验收成功后对子单发起处罚追缴",
        "丸掌柜：企业主账号或处罚权限账号；丸友集：工作室主账号；打手应用：打手子账号。",
        "已执行 S-FLOW-043 丸友集同意验收子单，并已执行 S-FLOW-049 丸掌柜同意验收；目标订单存在已验收成功的第1单子单，且页面展示子单处罚/处罚追缴入口。",
        "来源单号、订单标题、丸掌柜主单号、子单号、处罚对象、处罚类型、处罚金额、处罚原因、举证材料。",
        "1. 登录丸掌柜账号。\n2. 进入【丸掌柜】商家管理-订单管理。\n3. 按主单号、来源单号或订单标题查询目标订单。\n4. 点击订单编号第一列的主单号进入订单详情。\n5. 定位已验收成功的第1单子单。\n6. 点击该子单对应的【处罚追缴】或【子单处罚】入口。\n7. 选择处罚类型、处罚对象，填写处罚金额、处罚原因和说明。\n8. 按页面要求上传或关联举证材料。\n9. 提交处罚申请。\n10. 进入丸掌柜【子单处罚单】或订单处罚记录，按来源单号/子单号查询处罚记录。",
        "1. 丸掌柜可对已验收成功的目标子单发起处罚追缴。\n2. 丸掌柜生成子单处罚记录，处罚类型、金额、原因和目标子单一致。\n3. 原主单/子单验收完成状态不应被错误回退。",
        "1. 处罚记录的主单号、来源单号、子单号、责任方、金额在丸掌柜侧一致。\n2. 处罚只作用于目标子单，不误影响同主单其他子单。\n3. 后续由 S-FLOW-055 或 S-FLOW-056 验证跨端与流水影响。",
        "订单状态流转分支库；承接 S-FLOW-043/S-FLOW-049 后的已验收子单；可被处罚追缴、结算扣款后续流程引用。",
        "待自动化",
        "从 B-WZG-AFTER-ACCEPT-001 纳入 S-FLOW 体系；入口和审批/生效规则需按页面继续探测。",
    ],
    "S-FLOW-055": [
        "S-FLOW-055",
        "订单状态流转用例",
        "B-PENALTY-001：已完成子单处罚追缴",
        "处罚追缴",
        "已完成子单处罚追缴闭环",
        "处罚/追缴：管理员或审核账号；责任方：打手账号或工作室账号。",
        "已执行 S-FLOW-054，或存在已完成子单且已生成/可生成处罚追缴申请；打手收入已生成或可核对。",
        "已完成子单、处罚单号、责任方、处罚金额、处罚原因、审核结果、收入/流水记录。",
        "1. 登录有处罚权限的账号。\n2. 进入丸掌柜【子单处罚单】或处罚追缴审核入口。\n3. 按来源单号、子单号或处罚单号查询 S-FLOW-054 生成的处罚记录。\n4. 如存在审核流，打开处罚详情并审核通过。\n5. 记录处罚单状态、责任方、处罚金额和处理结果。\n6. 登录丸友集工作室账号，进入订单列表或处罚记录，查询目标子单。\n7. 登录打手账号，进入我的处罚/我的收入/账户流水，查询目标订单或处罚记录。\n8. 回到丸掌柜订单管理确认原订单终态仍保留。",
        "1. 已完成子单处罚追缴处理完成或进入页面实际生效状态。\n2. 原订单已完成状态不被错误回退。\n3. 丸掌柜、丸友集、打手侧可追溯到同一处罚单或处罚记录。\n4. 打手收入/流水体现扣减、追缴或页面实际金额影响。",
        "1. 处罚单号、订单号、子单号、责任方、金额、流水在各端一致。\n2. 处罚作为附加治理记录存在，不静默修改原履约记录。\n3. 不得重复扣款；重复处理应有明确限制或独立审批记录。",
        "承接 S-FLOW-054；对应 B-PENALTY-001；可被结算扣款、财务流水校验流程引用。",
        "待自动化",
        "已完成子单处罚后续处理/审核/跨端流水校验。",
    ],
    "S-FLOW-056": [
        "S-FLOW-056",
        "订单状态流转用例",
        "B-PENALTY-002：撤销完成子单处罚追缴",
        "处罚追缴",
        "撤销完成子单处罚追缴闭环",
        "丸掌柜：企业主账号或处罚权限账号；丸友集：工作室主账号；打手应用：打手子账号（按撤销节点需要）。",
        "存在撤销完成子单；该子单曾发生接单或履约动作；页面存在处罚入口，或业务规则明确拦截处罚。",
        "撤销完成子单号、撤销节点、责任方、处罚原因、处罚金额、拦截提示或处罚单号。",
        "1. 先执行任一撤销完成流程，记录撤销完成子单号、撤销节点和责任方。\n2. 登录有处罚权限的账号。\n3. 打开该撤销完成子单详情或进入子单处罚单入口。\n4. 查找【处罚追缴】或【子单处罚】入口。\n5. 如入口可用，选择责任方并填写金额、原因、凭证后提交。\n6. 如入口不可用，记录页面拦截提示。\n7. 如存在审核流，执行审核通过或拒绝。\n8. 责任方账号查看收入/流水/处罚记录。\n9. 再次尝试对同一子单同一原因发起处罚。",
        "1. 撤销完成子单若允许追缴，应生成明确处罚单和流水影响。\n2. 撤销完成子单若不允许追缴，应有明确拦截提示。\n3. 原撤销完成状态不被错误回退。\n4. 重复处罚不得造成重复扣款。",
        "1. 撤销状态、处罚状态、责任方、金额、流水一致。\n2. 允许追缴与明确拦截两种结果都必须可追溯。\n3. 重复处罚限制或二次审批记录清晰。",
        "对应 B-PENALTY-002；承接任一撤销完成链路；可被处罚追缴与财务流水校验流程引用。",
        "待自动化",
        "撤销完成子单处罚后续处理/拦截/重复限制校验。",
    ],
}


FLOWS = [
    (
        "FLOW-031",
        "丸掌柜验收完成后-子单处罚追缴闭环",
        "验收完成 + 处罚追缴",
        "主单已完成；子单已完成",
        "处罚追缴完成/处罚记录可追溯",
        ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-040", "S-FLOW-043", "S-FLOW-045", "S-FLOW-049", "S-FLOW-054", "S-FLOW-055"],
        "验证丸掌柜验收完成后，对已完成子单发起处罚并完成后续审核/流水校验。",
        "优先使用已走到 S-FLOW-049 的订单；处罚后仍可用于只读财务/流水校验。",
        "处罚会影响收入/流水，避免同一子单重复正向处罚。",
    ),
    (
        "FLOW-032",
        "已完成子单处罚追缴独立闭环",
        "处罚追缴独立校验",
        "已完成子单；收入已生成",
        "处罚追缴完成/流水影响一致",
        ["S-FLOW-055"],
        "复用任一已完成子单处罚记录，验证处理、审核、跨端和收入流水一致性。",
        "适合消费已存在处罚记录或 S-FLOW-054 产出的处罚单。",
        "若页面不存在审核流，以页面实际生效状态为准。",
    ),
    (
        "FLOW-033",
        "撤销完成子单处罚追缴闭环",
        "撤销完成 + 处罚追缴",
        "子单撤销完成",
        "允许处罚追缴或明确拦截",
        ["S-FLOW-056"],
        "验证撤销完成子单是否允许处罚追缴；允许则校验处罚记录和流水，不允许则校验明确拦截。",
        "可复用任一撤销完成子单；先做探测再决定是否生成处罚数据。",
        "该链路允许两种合法结果：处罚生效或明确拦截，但不能静默失败或重复扣款。",
    ),
]


def copy_row_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 append_unique(ws, key_col: int, key: str, row: list[object]) -> bool:
    for r in range(2, ws.max_row + 1):
        if str(ws.cell(r, key_col).value or "") == key:
            return False
    ws.append(row)
    copy_row_style(ws, ws.max_row - 1, ws.max_row)
    return True


def update_main_excel() -> None:
    workbook = load_workbook(MAIN)
    sheet = workbook["订单状态流转"]
    for case_id, row in CASES.items():
        append_unique(sheet, 1, case_id, row)
    workbook.save(MAIN)


def load_raw_cases(raw_sheet) -> dict[str, dict[str, str]]:
    result: dict[str, dict[str, str]] = {}
    for r in range(2, raw_sheet.max_row + 1):
        case_id = str(raw_sheet.cell(r, 1).value or "")
        if not case_id:
            continue
        result[case_id] = {
            "module": raw_sheet.cell(r, 2).value or "",
            "title": raw_sheet.cell(r, 3).value or "",
            "pre": raw_sheet.cell(r, 4).value or "",
            "expected": raw_sheet.cell(r, 5).value or "",
            "steps": raw_sheet.cell(r, 6).value or "",
        }
    for case_id, row in CASES.items():
        result[case_id] = {
            "module": row[3],
            "title": row[4],
            "pre": row[6],
            "expected": row[9],
            "steps": row[8],
        }
    return result


def update_flow_excel() -> None:
    workbook = load_workbook(FLOW)
    summary = workbook["链路流程线"]
    steps = workbook["链路步骤明细"]
    coverage = workbook["用例覆盖索引"]
    raw = workbook["原始用例索引"]
    notes = workbook["说明"]

    for case_id, row in CASES.items():
        append_unique(raw, 1, case_id, [case_id, row[3], row[4], row[6], row[9], row[8]])

    raw_cases = load_raw_cases(raw)

    def ref(case_id: str) -> str:
        return f"{case_id}({raw_cases.get(case_id, {}).get('title', '')})"

    def modules(case_ids: list[str]) -> str:
        seen: list[str] = []
        for case_id in case_ids:
            module = raw_cases.get(case_id, {}).get("module", "")
            if module and module not in seen:
                seen.append(module)
        return " -> ".join(seen)

    for flow_id, name, flow_type, start, end, case_ids, scene, reuse, note in FLOWS:
        append_unique(
            summary,
            1,
            flow_id,
            [
                flow_id,
                name,
                flow_type,
                start,
                end,
                " -> ".join(case_ids),
                " -> ".join(ref(case_id) for case_id in case_ids),
                modules(case_ids),
                scene,
                reuse,
                note,
            ],
        )
        existing_step_keys = {
            (str(steps.cell(r, 1).value or ""), str(steps.cell(r, 3).value or ""))
            for r in range(2, steps.max_row + 1)
        }
        for index, case_id in enumerate(case_ids, start=1):
            if (flow_id, case_id) in existing_step_keys:
                continue
            case = raw_cases.get(case_id, {})
            steps.append(
                [
                    flow_id,
                    index,
                    case_id,
                    case.get("module", ""),
                    case.get("title", ""),
                    case.get("pre", ""),
                    case.get("expected", ""),
                ]
            )
            copy_row_style(steps, steps.max_row - 1, steps.max_row)

    for case_id, row in CASES.items():
        occurrences = [flow[0] for flow in FLOWS if case_id in flow[5]]
        append_unique(
            coverage,
            1,
            case_id,
            [
                case_id,
                row[3],
                row[4],
                len(occurrences),
                "、".join(occurrences),
                "是" if occurrences else "否",
                "新增处罚追缴 S-FLOW 节点",
            ],
        )

    notes.append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} 追加 S-FLOW-054/055/056 处罚追缴链路"])
    copy_row_style(notes, notes.max_row - 1, notes.max_row)
    workbook.save(FLOW)


if __name__ == "__main__":
    update_main_excel()
    update_flow_excel()
    print(f"updated {MAIN}")
    print(f"updated {FLOW}")
