from __future__ import annotations

import json
import re
from pathlib import Path

import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill


ROOT = Path(__file__).resolve().parents[1]
CASE_JSON = ROOT / "scripts" / "order_status_flow_cases.json"
CASE_EXCEL = ROOT / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx"
OUTPUT_XLSX = ROOT / "runtime" / "s_flow_expected_status_audit.xlsx"
OUTPUT_JSON = ROOT / "runtime" / "s_flow_expected_status_audit.json"


STATUS_WORDS = [
    "待付款",
    "待接单",
    "待推单",
    "已推单",
    "服务中",
    "代练中",
    "待验收",
    "异常中",
    "售后中",
    "已售后",
    "已完成",
    "已取消",
    "取消订单",
    "申诉中",
    "申诉完成",
    "申诉被驳回",
    "驳回申诉",
]


IMPLEMENTED_RE = re.compile(r"test_s_flow_(\d{3})")


def implemented_case_ids() -> set[str]:
    case_ids: set[str] = set()
    for path in (ROOT / "e2e" / "tests" / "订单状态流转").rglob("test_s_flow_*.py"):
        match = IMPLEMENTED_RE.search(path.name)
        if match:
            case_ids.add(f"S-FLOW-{match.group(1)}")
    return case_ids


def load_cases() -> list[dict[str, str]]:
    if CASE_EXCEL.exists():
        workbook = openpyxl.load_workbook(CASE_EXCEL, data_only=True)
        sheet = workbook["订单状态流转"]
        headers = [sheet.cell(1, column).value for column in range(1, sheet.max_column + 1)]
        cases: list[dict[str, str]] = []
        for row in range(2, sheet.max_row + 1):
            item = {
                str(headers[column - 1]): str(sheet.cell(row, column).value or "")
                for column in range(1, sheet.max_column + 1)
            }
            if item.get("用例编号", "").startswith("S-FLOW-"):
                cases.append(item)
        return cases

    return json.loads(CASE_JSON.read_text(encoding="utf-8"))


def status_terms(text: str) -> list[str]:
    return [word for word in STATUS_WORDS if word in text]


def infer_expected(title: str, precondition: str, expected: str) -> tuple[str, str, str]:
    text = f"{title}\n{precondition}\n{expected}"
    main = ""
    sub = ""
    basis: list[str] = []

    if "创建主单未推单" in title:
        main, sub = "待接单", "待接单-待推单"
        basis.append("状态机 200/201：已支付未推单")
    elif "推单到丸友集" in title or "继续发子单" in title or "直接推单" in title:
        main, sub = "待接单", "待接单-已推单"
        basis.append("状态机 200/202：已推送给福通")
    elif "发起售后" in title:
        main, sub = "售后中", "售后中"
        basis.append("状态机 600/601：售后处理中")
    elif "处理售后" in title or "审核售后" in title:
        main, sub = "已售后", "已售后"
        basis.append("状态机 800/801：售后处理完毕")
    elif "取消售后" in title:
        main = "待接单" if "待接单" in precondition + title else "恢复到售后发起前主状态"
        if "已推单" in precondition + title:
            sub = "待接单-已推单"
        elif "待推单" in precondition + title or "未推单" in precondition + title:
            sub = "待接单-待推单"
        else:
            sub = "恢复到售后发起前子状态"
        basis.append("取消售后：恢复到发起售后前状态")
    elif "发起申诉" in title:
        if "已售后" in title + precondition:
            main, sub = "已售后", "已售后-申诉中"
            basis.append("状态机 800/802：已售后后发起申诉")
        else:
            main, sub = "已完成", "已完成-申诉中"
            basis.append("状态机 700/702：已完成后发起申诉")
    elif any(word in title for word in ["确认赔偿", "审核通过", "同意赔偿", "驳回申诉", "申诉被驳回"]):
        if "已售后" in precondition + expected + title:
            main, sub = "已售后或已完成（按平台详情实测收敛）", "申诉完成"
        else:
            main, sub = "已完成", "申诉完成"
        basis.append("状态机 703/803：申诉处理完毕")
    elif "处罚" in title or "追缴" in title:
        main, sub = "保持原终态", "保持已验收/已完成，叠加处罚/申诉处理状态"
        basis.append("处罚流程不应回退原订单履约状态")
    elif "撤销" in title:
        main = "按撤销所处阶段保持或进入异常/取消分支"
        sub = "目标子单进入撤销/取消终态"
        basis.append("撤销为子单分支动作，需要按阶段核实")
    elif "验收" in title or "完结" in title:
        main, sub = "已完成", "已完成"
        basis.append("状态机 700/701：验收通过订单完成")

    return main, sub, "；".join(basis)


def audit_case(case: dict[str, str], implemented: set[str]) -> dict[str, str]:
    case_id = case["用例编号"]
    title = case.get("用例标题", "")
    precondition = case.get("前置条件", "")
    expected = case.get("预期结果", "")
    assertion = case.get("跨端/状态断言", "")
    module = case.get("所属模块", "")
    inferred_main, inferred_sub, basis = infer_expected(title, precondition, expected)
    terms = status_terms(f"{expected}\n{assertion}")
    issues: list[str] = []

    if case_id in implemented:
        issues.append("已实现自动化，脚本内也需要持续保留订单详情主/子状态断言")
    else:
        issues.append("未实现自动化，生成脚本时必须补订单详情主/子状态断言")

    if inferred_main and inferred_main not in expected and "或" not in inferred_main and "按" not in inferred_main:
        issues.append(f"预期结果未明确主单终态「{inferred_main}」")
    if inferred_sub and all(part not in expected for part in inferred_sub.split("-")) and "保持" not in inferred_sub and "恢复" not in inferred_sub:
        issues.append(f"预期结果未明确子单终态「{inferred_sub}」")
    if "取消售后" in title and "取消订单" in expected:
        issues.append("疑似不一致：取消售后应恢复到售后发起前状态，不应默认写成目标子单取消订单")
    if ("处理售后" in title or "审核售后" in title) and "取消订单" in expected:
        issues.append("需复核：状态机 800/801 指向已售后，预期中的「取消订单」可能是旧分支口径")
    if "创建主单未推单" in title and "丸友集" in expected and "查询不到" not in expected:
        issues.append("需复核：未推单无子单时丸友集不应可见")
    main_appeal_mentions_external_approval = bool(re.search(
        r"丸友集.*(审批|审核|确认赔偿|驳回|处理)",
        expected,
    ))
    main_appeal_explicitly_excludes_wanyouji = any(
        phrase in expected
        for phrase in ["不涉及丸友集", "不需要登录丸友集", "丸友集侧不参与", "全流程不涉及丸友集"]
    )
    if (
        case_id in {"S-FLOW-050", "S-FLOW-051", "S-FLOW-052", "S-FLOW-053"}
        and main_appeal_mentions_external_approval
        and not main_appeal_explicitly_excludes_wanyouji
    ):
        issues.append("需复核：主单申诉 050~053 已实测为丸掌柜内部审核，不应要求丸友集审批")
    if not terms:
        issues.append("预期结果未抽取到明确状态词，建议补充主单/子单状态")

    return {
        "用例编号": case_id,
        "自动化实现": "已实现" if case_id in implemented else "未实现",
        "所属模块": module,
        "用例标题": title,
        "预期结果中状态词": "、".join(terms),
        "状态机推断主单终态": inferred_main,
        "状态机推断子单终态": inferred_sub,
        "推断依据": basis,
        "疑点/动作": "\n".join(issues),
        "原预期结果": expected,
        "原跨端/状态断言": assertion,
    }


def write_report(rows: list[dict[str, str]]) -> None:
    OUTPUT_XLSX.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_JSON.write_text(json.dumps(rows, ensure_ascii=False, indent=2), encoding="utf-8")

    workbook = openpyxl.Workbook()
    sheet = workbook.active
    sheet.title = "S-FLOW状态预期审计"
    headers = list(rows[0].keys()) if rows else []
    sheet.append(headers)
    for row in rows:
        sheet.append([row.get(header, "") for header in headers])

    header_fill = PatternFill("solid", fgColor="1F4E78")
    warning_fill = PatternFill("solid", fgColor="FFF2CC")
    for cell in sheet[1]:
        cell.fill = header_fill
        cell.font = Font(color="FFFFFF", bold=True)
        cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)

    for row in range(2, sheet.max_row + 1):
        action = str(sheet.cell(row, headers.index("疑点/动作") + 1).value or "")
        if "需复核" in action or "疑似不一致" in action or "未明确" in action:
            for col in range(1, sheet.max_column + 1):
                sheet.cell(row, col).fill = warning_fill
        for col in range(1, sheet.max_column + 1):
            sheet.cell(row, col).alignment = Alignment(vertical="top", wrap_text=True)

    widths = {
        "用例编号": 14,
        "自动化实现": 12,
        "所属模块": 16,
        "用例标题": 44,
        "预期结果中状态词": 28,
        "状态机推断主单终态": 24,
        "状态机推断子单终态": 30,
        "推断依据": 34,
        "疑点/动作": 54,
        "原预期结果": 70,
        "原跨端/状态断言": 70,
    }
    for index, header in enumerate(headers, start=1):
        sheet.column_dimensions[openpyxl.utils.get_column_letter(index)].width = widths.get(header, 20)

    sheet.freeze_panes = "A2"
    workbook.save(OUTPUT_XLSX)


def main() -> None:
    implemented = implemented_case_ids()
    cases = [case for case in load_cases() if case.get("用例编号", "").startswith("S-FLOW-")]
    rows = [audit_case(case, implemented) for case in cases]
    rows.sort(key=lambda row: row["用例编号"])
    write_report(rows)

    unimplemented = [row for row in rows if row["自动化实现"] == "未实现"]
    review = [row for row in rows if "需复核" in row["疑点/动作"] or "疑似不一致" in row["疑点/动作"]]
    print(f"审计完成：{len(rows)} 条 S-FLOW；未实现 {len(unimplemented)} 条；需重点复核 {len(review)} 条")
    print(f"XLSX: {OUTPUT_XLSX}")
    print(f"JSON: {OUTPUT_JSON}")
    for row in review[:12]:
        print(f"- {row['用例编号']} {row['用例标题']} :: {row['疑点/动作'].splitlines()[-1]}")


if __name__ == "__main__":
    main()
