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_case_row(
    case_id: str,
    module: str,
    title: str,
    login: str,
    precondition: str,
    data: str,
    action_steps: list[str],
    expected: str,
    assertion: str,
    relation: str,
    status: str = "待自动化",
    remark: str = "",
) -> list[str]:
    return [
        case_id,
        "订单状态流转用例",
        module,
        title,
        login,
        precondition,
        data,
        steps(action_steps),
        expected,
        assertion,
        relation,
        status,
        remark,
    ]


CASES = {
    "S-FLOW-072": status_case_row(
        "S-FLOW-072",
        "处罚追缴-丸友集发起",
        "【丸友集】已完成/撤销完成子单-发起追缴单",
        "丸友集：工作室主账号；打手应用：打手账号。",
        "丸友集工作室履约应用-订单列表存在子单状态为已完成或撤销完成的订单；目标子单操作列展示【处罚追缴】入口；目标打手账号可登录打手应用-我的处罚。",
        "主单号、来源单号、子单号、订单标题、打手账号、追缴原因、追缴内容、追缴时限、处罚金额、证据图片。",
        [
            "登录丸友集工作室主账号。",
            "进入丸友集-工作室履约应用-订单列表。",
            "按主单号、来源单号或订单标题搜索目标订单。",
            "打开订单详情或子单列表，定位状态为已完成或撤销完成的目标子单。",
            "点击目标子单操作列的【处罚追缴】。",
            "在处罚追缴弹框中选择【追缴单】。",
            "勾选追缴原因，填写追缴内容、追缴时限、处罚金额，并按页面要求上传证据图片。",
            "提交处罚追缴。",
            "登录打手账号，进入打手应用-我的处罚。",
            "切换到【追缴单】页签，按子单号、主单号或处罚/追缴单号查询新生成的追缴单。",
        ],
        "丸友集侧成功生成追缴单；打手应用-我的处罚-追缴单页签出现对应记录，状态为进行中/待完成等待打手处理状态，追缴内容、追缴时限和处罚金额与丸友集提交信息一致。",
        "丸友集订单详情中的原子单状态仍保持已完成或撤销完成；打手我的处罚中追缴单关联的主单号、子单号、打手账号、追缴金额与丸友集侧一致。",
        "处罚追缴主分支-追缴流程起点；后续承接 S-FLOW-073；被 FLOW-044 引用。",
        remark="2026-06-29 根据用户补充新增：丸友集侧对子单终态发起追缴单。",
    ),
    "S-FLOW-073": status_case_row(
        "S-FLOW-073",
        "处罚追缴-打手处理",
        "【打手】我的处罚-完成追缴单",
        "打手应用：打手账号；丸友集：工作室主账号（用于结果回查）。",
        "已执行 S-FLOW-072；打手应用-我的处罚-追缴单页签存在状态为进行中/待完成的目标追缴单。",
        "处罚/追缴单号、主单号、来源单号、子单号、追缴内容、完成说明、完成证据图片。",
        [
            "登录目标打手账号。",
            "进入打手应用-我的处罚。",
            "切换到【追缴单】页签。",
            "按处罚/追缴单号、子单号、主单号或来源单号查询 S-FLOW-072 生成的追缴单。",
            "点击操作列【查看】。",
            "在追缴单详情中核对追缴原因、追缴内容、追缴时限和处罚金额。",
            "点击【提交完成】或页面等价完成入口。",
            "填写完成说明，并按页面要求上传完成证据图片。",
            "提交完成追缴。",
            "刷新我的处罚-追缴单页签，重新查询目标记录。",
            "登录丸友集工作室主账号，进入我的处罚/订单详情或相关记录入口回查追缴单状态。",
        ],
        "打手提交完成追缴成功；追缴单状态不再停留在进行中，应进入已完成、待确认或页面实际完成态；详情可回看打手提交的完成说明和证据。",
        "打手侧追缴单状态、完成说明、证据图片与丸友集侧追缴记录一致；原订单子单终态不应被追缴完成动作错误回退。",
        "承接 S-FLOW-072；处罚追缴主分支-追缴流程收束；被 FLOW-044 引用。",
        remark="追缴单需要打手在打手应用-我的处罚中继续完成追缴内容。",
    ),
    "S-FLOW-074": status_case_row(
        "S-FLOW-074",
        "处罚追缴-丸友集发起",
        "【丸友集】已完成/撤销完成子单-发起处罚单并在打手侧查看金额",
        "丸友集：工作室主账号；打手应用：打手账号。",
        "丸友集工作室履约应用-订单列表存在子单状态为已完成或撤销完成的订单；目标子单操作列展示【处罚追缴】入口；目标打手账号可登录打手应用-我的处罚。",
        "主单号、来源单号、子单号、订单标题、打手账号、处罚原因、处罚金额、证据图片。",
        [
            "登录丸友集工作室主账号。",
            "进入丸友集-工作室履约应用-订单列表。",
            "按主单号、来源单号或订单标题搜索目标订单。",
            "打开订单详情或子单列表，定位状态为已完成或撤销完成的目标子单。",
            "点击目标子单操作列的【处罚追缴】。",
            "在处罚追缴弹框中选择【处罚单】。",
            "勾选处罚原因，填写处罚金额，并按页面要求上传证据图片或补充说明。",
            "提交处罚单。",
            "登录目标打手账号，进入打手应用-我的处罚。",
            "切换到【罚单】页签。",
            "按子单号、主单号或处罚单号查询新生成的处罚单。",
            "点击操作列【查看】，核对处罚金额和状态。",
        ],
        "丸友集侧成功生成处罚单；打手应用-我的处罚-罚单页签出现对应处罚单，状态为已处罚或页面实际生效状态；处罚金额与丸友集提交金额一致。",
        "处罚单为直接处罚金额，不要求打手继续完成追缴任务；打手侧处罚金额、主单号、子单号、打手账号与丸友集侧一致；原订单子单终态不应被处罚动作错误回退。",
        "处罚追缴主分支-处罚流程；被 FLOW-045 引用。",
        remark="处罚单前半段复用处罚追缴入口，只是在弹框中选择处罚单；结果以打手我的处罚查看处罚金额为准。",
    ),
}


FLOW_ROWS = {
    "FLOW-044": [
        "FLOW-044",
        "丸友集子单终态处罚追缴-追缴单流程",
        "处罚追缴-追缴流程",
        "子单已完成或撤销完成；丸友集侧可操作处罚追缴",
        "打手完成追缴；追缴单状态完成/待确认",
        "S-FLOW-072 -> S-FLOW-073",
        "S-FLOW-072(【丸友集】已完成/撤销完成子单-发起追缴单) -> S-FLOW-073(【打手】我的处罚-完成追缴单)",
        "处罚追缴-丸友集发起 -> 处罚追缴-打手处理",
        "验证子单状态为已完成或撤销完成时，丸友集可创建追缴单，并由打手在打手应用-我的处罚中提交完成追缴。",
        "可复用 S-FLOW-049 形成的已完成子单，或复用任一撤销完成/撤销结束子单；追缴单数据应避免与处罚单分支共用同一子单重复生效。",
        "追缴单不是直接生效的罚单，必须覆盖打手侧继续完成追缴内容；若平台存在丸友集确认环节，应在后续脚本中补充回查。",
    ],
    "FLOW-045": [
        "FLOW-045",
        "丸友集子单终态处罚追缴-处罚单流程",
        "处罚追缴-处罚流程",
        "子单已完成或撤销完成；丸友集侧可操作处罚追缴",
        "处罚单生成；打手侧可查看处罚金额",
        "S-FLOW-074",
        "S-FLOW-074(【丸友集】已完成/撤销完成子单-发起处罚单并在打手侧查看金额)",
        "处罚追缴-丸友集发起",
        "验证子单状态为已完成或撤销完成时，丸友集可创建处罚单；处罚单为直接处罚金额，结果以打手应用-我的处罚-罚单中金额展示为准。",
        "可复用 S-FLOW-049 形成的已完成子单，或复用任一撤销完成/撤销结束子单；处罚单分支与追缴单分支建议使用不同订单或不同子单。",
        "处罚单不要求打手完成追缴任务；校验重点是罚单类型、处罚金额、已处罚状态和关联订单一致性。",
    ],
}


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
    ws.row_dimensions[dst_row].height = ws.row_dimensions[src_row].height


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


def first_blank_or_append_row(ws) -> int:
    for row in range(2, ws.max_row + 1):
        if not str(ws.cell(row, 1).value or "").strip():
            return row
    return ws.max_row + 1


def write_values(ws, row: int, values: list[object]) -> None:
    for column, value in enumerate(values, start=1):
        ws.cell(row, column).value = value
    ws.row_dimensions[row].height = None
    for column in range(1, len(values) + 1):
        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_after_anchor(ws, key: str, values: list[object], anchor: str | None = None) -> None:
    row = find_row(ws, key)
    if row:
        write_values(ws, row, values)
        return
    if anchor:
        anchor_row = find_row(ws, anchor)
        if anchor_row:
            row = anchor_row + 1
            ws.insert_rows(row)
            copy_row_style(ws, anchor_row, row)
            write_values(ws, row, values)
            return
    row = first_blank_or_append_row(ws)
    if row > ws.max_row:
        ws.append([])
    copy_row_style(ws, max(2, row - 1), row)
    write_values(ws, row, values)


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 "").strip() in keys:
            ws.delete_rows(row)


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


def update_main_workbook() -> None:
    backup(MAIN_WORKBOOK)
    wb = load_workbook(MAIN_WORKBOOK)
    status_ws = wb["订单状态流转"]
    anchor = "S-FLOW-071"
    for case_id in ["S-FLOW-072", "S-FLOW-073", "S-FLOW-074"]:
        upsert_after_anchor(status_ws, case_id, CASES[case_id], anchor)
        anchor = case_id

    combo_ws = wb["组合流程用例"]
    anchor_flow = "FLOW-043"
    for flow_id in ["FLOW-044", "FLOW-045"]:
        flow = FLOW_ROWS[flow_id]
        row = [
            flow_id,
            "组合流程用例",
            flow[7],
            flow[1],
            "丸友集：工作室主账号；打手应用：打手账号。",
            flow[3],
            "主单号、来源单号、子单号、订单标题、打手账号、处罚/追缴金额、证据图片。",
            flow[6],
            flow[4],
            flow[8],
            flow_id,
            "待自动化",
            flow[10],
        ]
        upsert_after_anchor(combo_ws, flow_id, row, anchor_flow)
        anchor_flow = flow_id
    wb.save(MAIN_WORKBOOK)
    print(f"updated {MAIN_WORKBOOK}")


def update_flow_workbook() -> None:
    backup(FLOW_WORKBOOK)
    wb = load_workbook(FLOW_WORKBOOK)
    summary_ws = wb["链路流程线"]
    step_ws = wb["链路步骤明细"]
    coverage_ws = wb["用例覆盖索引"]
    raw_ws = wb["原始用例索引"]
    note_ws = wb["说明"]

    anchor = "S-FLOW-071"
    for case_id in ["S-FLOW-072", "S-FLOW-073", "S-FLOW-074"]:
        row = [
            case_id,
            CASES[case_id][2],
            CASES[case_id][3],
            CASES[case_id][5],
            CASES[case_id][8],
            CASES[case_id][7],
        ]
        upsert_after_anchor(raw_ws, case_id, row, anchor)
        coverage_row = [
            case_id,
            CASES[case_id][2],
            CASES[case_id][3],
            1,
            "FLOW-044" if case_id in {"S-FLOW-072", "S-FLOW-073"} else "FLOW-045",
            "是",
            "新增丸友集侧处罚追缴主分支用例。",
        ]
        upsert_after_anchor(coverage_ws, case_id, coverage_row, anchor)
        anchor = case_id

    delete_rows_by_first_col(step_ws, {"FLOW-044", "FLOW-045"})
    anchor_flow = "FLOW-043"
    for flow_id in ["FLOW-044", "FLOW-045"]:
        upsert_after_anchor(summary_ws, flow_id, FLOW_ROWS[flow_id], anchor_flow)
        anchor_flow = flow_id
        case_ids = ["S-FLOW-072", "S-FLOW-073"] if flow_id == "FLOW-044" else ["S-FLOW-074"]
        for index, case_id in enumerate(case_ids, start=1):
            case = CASES[case_id]
            step_ws.append([flow_id, index, case_id, case[2], case[3], case[5], case[8]])
            copy_row_style(step_ws, max(2, step_ws.max_row - 1), step_ws.max_row)

    note_ws.append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} 新增丸友集侧处罚追缴主分支：追缴流程和处罚流程。"])
    copy_row_style(note_ws, max(2, note_ws.max_row - 1), note_ws.max_row)
    wb.save(FLOW_WORKBOOK)
    print(f"updated {FLOW_WORKBOOK}")


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


if __name__ == "__main__":
    main()
