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"


CASE_UPDATES = {
    "S-FLOW-054": {
        "source": "B-WZG-AFTER-ACCEPT-001：丸掌柜验收第1单子单后处罚第1单",
        "module": "订单验收后处罚-上游发起",
        "title": "【丸掌柜】子单验收成功后对子单发起处罚追缴",
        "role": "丸掌柜：企业主账号或处罚权限账号；丸友集：工作室主账号；打手应用：打手子账号。",
        "pre": "已执行 S-FLOW-043 丸友集同意验收子单，并已执行 S-FLOW-049 丸掌柜同意验收；目标订单存在已验收成功的子单，且页面展示子单处罚/处罚追缴入口。",
        "data": "来源单号、订单标题、丸掌柜主单号、子单号、处罚对象、处罚类型、处罚金额、处罚原因、举证材料。",
        "steps": "1. 登录丸掌柜账号。\n2. 进入【丸掌柜】商家管理-订单管理。\n3. 按主单号、来源单号或订单标题查询目标订单。\n4. 点击订单编号第一列的主单号进入订单详情。\n5. 定位已验收成功的目标子单。\n6. 点击该子单对应的【处罚追缴】或【子单处罚】入口。\n7. 选择处罚类型、处罚对象，填写处罚金额、处罚原因和说明。\n8. 按页面要求上传或关联举证材料。\n9. 提交处罚申请。\n10. 进入丸掌柜【子单处罚单】或订单处罚记录，按来源单号/子单号查询处罚记录。",
        "expected": "1. 丸掌柜可对已验收成功的目标子单发起处罚追缴。\n2. 丸掌柜生成子单处罚记录，处罚类型、金额、原因和目标子单一致。\n3. 原主单/子单验收完成状态不应被错误回退。\n4. 丸友集侧出现待处理处罚/申诉记录。",
        "assertion": "1. 处罚记录的主单号、来源单号、子单号、责任方、金额在丸掌柜侧一致。\n2. 处罚只作用于目标子单，不误影响同主单其他子单。\n3. 后续由 S-FLOW-055 或 S-FLOW-056 验证丸友集处理结果。",
        "relation": "订单状态流转分支库；承接 S-FLOW-043/S-FLOW-049 后的已验收子单；后续分支为 S-FLOW-055/S-FLOW-056。",
        "status": "待自动化",
        "note": "从 B-WZG-AFTER-ACCEPT-001 纳入 S-FLOW 体系；入口和字段需按页面继续探测。",
    },
    "S-FLOW-055": {
        "source": "B-PENALTY-001：子单处罚追缴-丸友集同意赔偿",
        "module": "处罚追缴-丸友集处理",
        "title": "【丸友集】同意赔偿后【丸掌柜】子单申诉处理完成",
        "role": "丸友集：工作室主账号或处罚处理权限账号；丸掌柜：企业主账号或处罚权限账号；打手应用：打手子账号。",
        "pre": "已执行 S-FLOW-054，丸掌柜已对子单发起处罚追缴，丸友集侧出现待处理处罚/申诉记录。",
        "data": "来源单号、订单标题、子单号、处罚单号、赔偿/处罚金额、处理备注、举证材料。",
        "steps": "1. 登录丸友集账号。\n2. 进入处罚/申诉处理入口。\n3. 按来源单号、订单标题、子单号或处罚单号查询 S-FLOW-054 生成的记录。\n4. 打开处罚/申诉详情。\n5. 点击【同意赔偿】或页面对应同意处理入口。\n6. 填写赔偿金额、处理备注和必要凭证。\n7. 提交处理结果。\n8. 登录丸掌柜账号进入子单处罚单或订单详情，查询目标记录。\n9. 登录打手账号进入我的处罚/我的收入，查询处罚或金额影响记录。",
        "expected": "1. 丸友集处理结果为同意赔偿或页面对应赔偿完成状态。\n2. 丸掌柜侧子单申诉/处罚处理完成，可继续申请处罚。\n3. 原主单/子单已完成状态不被错误回退。\n4. 打手侧我的处罚/我的收入体现对应赔偿或扣减影响。",
        "assertion": "1. 处罚单号、子单号、责任方、赔偿/处罚金额、处理结果在丸掌柜、丸友集、打手侧一致。\n2. 处理完成后仍允许按平台规则继续申请处罚。\n3. 同一处理结果不得重复生效导致重复扣款。",
        "relation": "承接 S-FLOW-054；对应真实平台处罚追缴同意赔偿分支；可继续申请处罚。",
        "status": "待自动化",
        "note": "修正自原 B-PENALTY-001；不再描述为抽象财务审核闭环。",
    },
    "S-FLOW-056": {
        "source": "B-PENALTY-002：子单处罚追缴-丸友集驳回申诉",
        "module": "处罚追缴-丸友集处理",
        "title": "【丸友集】驳回申诉后【丸掌柜】子单申诉被驳回",
        "role": "丸友集：工作室主账号或处罚处理权限账号；丸掌柜：企业主账号或处罚权限账号。",
        "pre": "已执行 S-FLOW-054，丸掌柜已对子单发起处罚追缴，丸友集侧出现待处理处罚/申诉记录。",
        "data": "来源单号、订单标题、子单号、处罚单号、驳回原因、处理备注。",
        "steps": "1. 登录丸友集账号。\n2. 进入处罚/申诉处理入口。\n3. 按来源单号、订单标题、子单号或处罚单号查询 S-FLOW-054 生成的记录。\n4. 打开处罚/申诉详情。\n5. 点击【驳回申诉】或页面对应驳回入口。\n6. 填写驳回原因和处理备注。\n7. 提交处理结果。\n8. 登录丸掌柜账号进入子单处罚单或订单详情，查询目标记录。\n9. 记录丸掌柜侧子单申诉被驳回状态及是否可继续申请处罚。",
        "expected": "1. 丸友集处理结果为驳回申诉或页面对应驳回状态。\n2. 丸掌柜侧子单申诉被驳回，可继续申请处罚。\n3. 原主单/子单已完成状态不被错误回退。\n4. 不生成同意赔偿造成的扣减或赔偿流水。",
        "assertion": "1. 处罚单号、子单号、驳回原因、处理结果在丸掌柜和丸友集侧一致。\n2. 驳回后仍允许按平台规则继续申请处罚。\n3. 驳回分支不得产生同意赔偿的资金影响。",
        "relation": "承接 S-FLOW-054；对应真实平台处罚追缴驳回申诉分支；可继续申请处罚。",
        "status": "待自动化",
        "note": "修正自原 B-PENALTY-002；实际平台没有撤销完成子单处罚操作。",
    },
}


BRANCH_CASE_UPDATES = {
    "B-PENALTY-001": {
        "source": "S-FLOW-054 后续分支",
        "module": "处罚追缴-丸友集处理",
        "title": "子单处罚追缴-丸友集同意赔偿",
        "role": CASE_UPDATES["S-FLOW-055"]["role"],
        "pre": CASE_UPDATES["S-FLOW-055"]["pre"],
        "data": CASE_UPDATES["S-FLOW-055"]["data"],
        "steps": CASE_UPDATES["S-FLOW-055"]["steps"],
        "expected": CASE_UPDATES["S-FLOW-055"]["expected"],
        "assertion": CASE_UPDATES["S-FLOW-055"]["assertion"],
        "relation": "映射 S-FLOW-055；被处罚追缴链路 FLOW-031 引用。",
        "status": "待自动化",
        "note": "按实测平台修正：丸友集同意赔偿，丸掌柜子单申诉处理完成，可继续申请处罚。",
    },
    "B-PENALTY-002": {
        "source": "S-FLOW-054 后续分支",
        "module": "处罚追缴-丸友集处理",
        "title": "子单处罚追缴-丸友集驳回申诉",
        "role": CASE_UPDATES["S-FLOW-056"]["role"],
        "pre": CASE_UPDATES["S-FLOW-056"]["pre"],
        "data": CASE_UPDATES["S-FLOW-056"]["data"],
        "steps": CASE_UPDATES["S-FLOW-056"]["steps"],
        "expected": CASE_UPDATES["S-FLOW-056"]["expected"],
        "assertion": CASE_UPDATES["S-FLOW-056"]["assertion"],
        "relation": "映射 S-FLOW-056；被处罚追缴链路 FLOW-032 引用。",
        "status": "待自动化",
        "note": "按实测平台修正：实际平台没有撤销完成子单处罚操作。",
    },
}


FLOWS = {
    "FLOW-031": {
        "name": "丸掌柜子单处罚追缴-丸友集同意赔偿",
        "type": "处罚追缴同意赔偿",
        "start": "主单已完成；子单已完成",
        "end": "子单申诉处理完成；可继续申请处罚",
        "case_ids": ["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"],
        "scene": "验证丸掌柜验收成功后对子单发起处罚追缴，丸友集同意赔偿，丸掌柜侧处理完成。",
        "reuse": "优先使用已走到 S-FLOW-049 的订单；处罚同意赔偿后可继续只读核对或再次申请处罚。",
        "note": "同意赔偿会产生处罚/赔偿结果，避免同一子单重复生效造成重复扣款。",
    },
    "FLOW-032": {
        "name": "丸掌柜子单处罚追缴-丸友集驳回申诉",
        "type": "处罚追缴驳回申诉",
        "start": "主单已完成；子单已完成",
        "end": "子单申诉被驳回；可继续申请处罚",
        "case_ids": ["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-056"],
        "scene": "验证丸掌柜验收成功后对子单发起处罚追缴，丸友集驳回申诉，丸掌柜侧显示被驳回。",
        "reuse": "需要与同意赔偿分支分开用订单；驳回后可继续申请处罚。",
        "note": "驳回分支不应产生同意赔偿的资金影响。",
    },
}


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 case_row(case_id: str, data: dict[str, str], level: str = "订单状态流转用例") -> list[str]:
    return [
        case_id,
        level,
        data["source"],
        data["module"],
        data["title"],
        data["role"],
        data["pre"],
        data["data"],
        data["steps"],
        data["expected"],
        data["assertion"],
        data["relation"],
        data["status"],
        data["note"],
    ]


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 "") == key:
            return row
    return None


def upsert_row(ws, key: str, values: list[str]) -> None:
    row = find_row(ws, key)
    if row is None:
        ws.append(values)
        copy_row_style(ws, ws.max_row - 1, ws.max_row)
        return
    for column, value in enumerate(values, start=1):
        ws.cell(row, column).value = value


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


def update_main() -> None:
    workbook = load_workbook(MAIN)
    status_sheet = workbook["订单状态流转"]
    for case_id, data in CASE_UPDATES.items():
        upsert_row(status_sheet, case_id, case_row(case_id, data))

    list_sheet = workbook["用例测试列表"]
    for case_id, data in BRANCH_CASE_UPDATES.items():
        upsert_row(list_sheet, case_id, case_row(case_id, data, level="分支小用例"))

    combo = workbook["组合流程用例"]
    updates = {
        "F-PENALTY-001": {
            "source": "S-FLOW-054 -> S-FLOW-055",
            "title": "子单处罚追缴-丸友集同意赔偿流程",
            "pre": CASE_UPDATES["S-FLOW-054"]["pre"],
            "steps": "1. 先执行正常履约至 S-FLOW-049。\n2. 执行 S-FLOW-054，由丸掌柜对子单发起处罚追缴。\n3. 执行 S-FLOW-055，由丸友集同意赔偿。\n4. 回到丸掌柜、丸友集、打手侧核对处罚处理完成和金额影响。",
            "expected": CASE_UPDATES["S-FLOW-055"]["expected"],
            "assertion": CASE_UPDATES["S-FLOW-055"]["assertion"],
            "relation": "FLOW-031",
            "note": "按真实平台修正处罚同意赔偿分支。",
        },
        "F-PENALTY-002": {
            "source": "S-FLOW-054 -> S-FLOW-056",
            "title": "子单处罚追缴-丸友集驳回申诉流程",
            "pre": CASE_UPDATES["S-FLOW-054"]["pre"],
            "steps": "1. 先执行正常履约至 S-FLOW-049。\n2. 执行 S-FLOW-054，由丸掌柜对子单发起处罚追缴。\n3. 执行 S-FLOW-056，由丸友集驳回申诉。\n4. 回到丸掌柜、丸友集侧核对驳回状态，并确认可继续申请处罚。",
            "expected": CASE_UPDATES["S-FLOW-056"]["expected"],
            "assertion": CASE_UPDATES["S-FLOW-056"]["assertion"],
            "relation": "FLOW-032",
            "note": "按真实平台修正：实际平台没有撤销完成子单处罚操作。",
        },
    }
    for case_id, update in updates.items():
        row = find_row(combo, case_id)
        if row:
            combo.cell(row, 3).value = update["source"]
            combo.cell(row, 5).value = update["title"]
            combo.cell(row, 7).value = update["pre"]
            combo.cell(row, 9).value = update["steps"]
            combo.cell(row, 10).value = update["expected"]
            combo.cell(row, 11).value = update["assertion"]
            combo.cell(row, 12).value = update["relation"]
            combo.cell(row, 14).value = update["note"]

    workbook.save(MAIN)


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


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

    for case_id, data in CASE_UPDATES.items():
        upsert_row(raw, case_id, [case_id, data["module"], data["title"], data["pre"], data["expected"], data["steps"]])

    # Remove obsolete revoked-completed-sub-order penalty chain.
    delete_rows_by_first_col(summary, {"FLOW-033"})
    delete_rows_by_first_col(steps, {"FLOW-031", "FLOW-032", "FLOW-033"})
    delete_rows_by_first_col(coverage, {"S-FLOW-054", "S-FLOW-055", "S-FLOW-056"})

    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, data in FLOWS.items():
        upsert_row(
            summary,
            flow_id,
            [
                flow_id,
                data["name"],
                data["type"],
                data["start"],
                data["end"],
                " -> ".join(data["case_ids"]),
                " -> ".join(ref(case_id) for case_id in data["case_ids"]),
                modules(data["case_ids"]),
                data["scene"],
                data["reuse"],
                data["note"],
            ],
        )
        for index, case_id in enumerate(data["case_ids"], start=1):
            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, data in CASE_UPDATES.items():
        occurrences = [flow_id for flow_id, flow in FLOWS.items() if case_id in flow["case_ids"]]
        coverage.append(
            [
                case_id,
                data["module"],
                data["title"],
                len(occurrences),
                "、".join(occurrences),
                "是" if occurrences else "否",
                "按实测平台修正处罚追缴分支；撤销完成子单处罚已移除",
            ]
        )
        copy_row_style(coverage, coverage.max_row - 1, coverage.max_row)

    notes.append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} 修正 S-FLOW-055/056 为丸友集同意赔偿/驳回申诉，移除撤销完成子单处罚链路"])
    copy_row_style(notes, notes.max_row - 1, notes.max_row)
    workbook.save(FLOW)


if __name__ == "__main__":
    update_main()
    update_flow()
    print(f"corrected {MAIN}")
    print(f"corrected {FLOW}")
