from __future__ import annotations

import re
import shutil
from copy import copy
from datetime import datetime
from pathlib import Path

from openpyxl import load_workbook
from openpyxl.utils import get_column_letter


ROOT = Path(__file__).resolve().parents[1]
MAIN_XLSX = ROOT / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx"
FLOW_XLSX = ROOT / "订单状态流转_链路流程线整理.xlsx"
TEST_ROOT = ROOT / "e2e" / "tests" / "订单状态流转"
BACKUP_DIR = ROOT / "runtime" / "excel_backups"
OBSOLETE_DIR_BACKUP = ROOT / "runtime" / "order_status_obsolete_module_dirs"


def now_tag() -> str:
    return datetime.now().strftime("%Y%m%d_%H%M%S")


def backup(path: Path, tag: str) -> Path:
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    target = BACKUP_DIR / f"{path.stem}.{tag}{path.suffix}"
    shutil.copy2(path, target)
    return target


def headers(ws) -> dict[str, int]:
    return {str(ws.cell(1, col).value).strip(): col for col in range(1, ws.max_column + 1) if ws.cell(1, col).value}


def read_status_cases() -> dict[str, dict[str, str]]:
    wb = load_workbook(MAIN_XLSX, data_only=False)
    ws = wb["订单状态流转"]
    hs = headers(ws)
    result: dict[str, dict[str, str]] = {}
    for row in range(2, ws.max_row + 1):
        case_id = ws.cell(row, hs["用例编号"]).value
        if isinstance(case_id, str) and case_id.startswith("S-FLOW-"):
            result[case_id] = {
                "module": str(ws.cell(row, hs["所属模块"]).value or "").strip(),
                "title": str(ws.cell(row, hs["用例标题"]).value or "").strip(),
                "precondition": str(ws.cell(row, hs["前置条件"]).value or "").strip(),
                "expected": str(ws.cell(row, hs["预期结果"]).value or "").strip(),
                "steps": str(ws.cell(row, hs["执行步骤"]).value or "").strip(),
            }
    wb.close()
    return result


def remove_source_column() -> list[str]:
    wb = load_workbook(MAIN_XLSX)
    changed: list[str] = []
    for ws in wb.worksheets:
        hs = headers(ws)
        col = hs.get("来源/组合依据")
        if col:
            ws.delete_cols(col, 1)
            changed.append(ws.title)
            ws.freeze_panes = "A2"
            for row in ws.iter_rows():
                for cell in row:
                    cell.alignment = copy(cell.alignment)
                    cell.alignment = cell.alignment.copy(wrap_text=True, vertical="top")
            for row in range(2, ws.max_row + 1):
                ws.row_dimensions[row].height = None
            for col_idx in range(1, ws.max_column + 1):
                ws.column_dimensions[get_column_letter(col_idx)].width = min(
                    max(ws.column_dimensions[get_column_letter(col_idx)].width or 12, 12),
                    60,
                )
    if changed:
        wb.save(MAIN_XLSX)
    wb.close()
    return changed


def sync_flow_workbook(cases: dict[str, dict[str, str]]) -> dict[str, int]:
    wb = load_workbook(FLOW_XLSX)
    counts = {"module": 0, "title": 0, "precondition": 0, "expected": 0, "steps": 0}
    for ws in wb.worksheets:
        hs = headers(ws)
        id_col = hs.get("用例编号")
        if not id_col:
            continue
        for row in range(2, ws.max_row + 1):
            case_id = ws.cell(row, id_col).value
            if not isinstance(case_id, str) or case_id not in cases:
                continue
            data = cases[case_id]
            mapping = {
                "所属模块": ("module", data["module"]),
                "用例标题": ("title", data["title"]),
                "前置条件": ("precondition", data["precondition"]),
                "预期结果": ("expected", data["expected"]),
                "执行步骤": ("steps", data["steps"]),
            }
            for header, (counter, value) in mapping.items():
                col = hs.get(header)
                if col and ws.cell(row, col).value != value:
                    ws.cell(row, col).value = value
                    counts[counter] += 1
    # Recompute involved module summaries in the flow-line sheet from the case sequence.
    if "链路流程线" in wb.sheetnames:
        ws = wb["链路流程线"]
        hs = headers(ws)
        seq_col = hs.get("用例编号序列")
        mod_col = hs.get("涉及模块")
        if seq_col and mod_col:
            for row in range(2, ws.max_row + 1):
                seq = str(ws.cell(row, seq_col).value or "")
                ids = re.findall(r"S-FLOW-\d{3}", seq)
                modules: list[str] = []
                for case_id in ids:
                    module = cases.get(case_id, {}).get("module", "")
                    if module and module not in modules:
                        modules.append(module)
                value = " -> ".join(modules)
                if value and ws.cell(row, mod_col).value != value:
                    ws.cell(row, mod_col).value = value
                    counts["module"] += 1
    wb.save(FLOW_XLSX)
    wb.close()
    return counts


def ensure_init(directory: Path) -> None:
    directory.mkdir(parents=True, exist_ok=True)
    init = directory / "__init__.py"
    if not init.exists():
        init.write_text("", encoding="utf-8")


def sync_test_locations(cases: dict[str, dict[str, str]]) -> list[str]:
    moved: list[str] = []
    ensure_init(TEST_ROOT)
    for path in sorted(TEST_ROOT.rglob("test_s_flow_*.py")):
        match = re.search(r"test_s_flow_(\d+)", path.name)
        if not match:
            continue
        case_id = f"S-FLOW-{int(match.group(1)):03d}"
        module = cases.get(case_id, {}).get("module")
        if not module:
            continue
        target_dir = TEST_ROOT / module
        ensure_init(target_dir)
        target = target_dir / path.name
        if path.resolve() == target.resolve():
            continue
        if target.exists():
            raise RuntimeError(f"target already exists: {target}")
        path.rename(target)
        moved.append(f"{case_id}: {path.relative_to(ROOT)} -> {target.relative_to(ROOT)}")
    return moved


def sync_module_directories(cases: dict[str, dict[str, str]], tag: str) -> list[str]:
    expected_modules: list[str] = []
    for data in cases.values():
        module = data.get("module", "")
        if module and module not in expected_modules:
            expected_modules.append(module)

    for module in expected_modules:
        ensure_init(TEST_ROOT / module)

    archived: list[str] = []
    backup_root = OBSOLETE_DIR_BACKUP / tag
    for child in sorted(TEST_ROOT.iterdir()):
        if not child.is_dir() or child.name == "__pycache__":
            continue
        if child.name in expected_modules:
            continue
        test_files = list(child.rglob("test_s_flow_*.py"))
        if test_files:
            raise RuntimeError(f"obsolete module dir still contains test files: {child}")
        backup_root.mkdir(parents=True, exist_ok=True)
        target = backup_root / child.name
        if target.exists():
            raise RuntimeError(f"obsolete module backup already exists: {target}")
        child.rename(target)
        archived.append(f"{child.relative_to(ROOT)} -> {target.relative_to(ROOT)}")
    return archived


def main() -> None:
    tag = now_tag()
    main_backup = backup(MAIN_XLSX, tag)
    flow_backup = backup(FLOW_XLSX, tag)
    cases = read_status_cases()
    moved = sync_test_locations(cases)
    archived_dirs = sync_module_directories(cases, tag)
    removed_sheets = remove_source_column()
    flow_counts = sync_flow_workbook(cases)
    print(f"backed up main workbook: {main_backup}")
    print(f"backed up flow workbook: {flow_backup}")
    print("removed 来源/组合依据 from sheets:", ", ".join(removed_sheets) or "none")
    print("moved test files:")
    for item in moved:
        print("  " + item)
    print("archived obsolete module dirs:")
    for item in archived_dirs:
        print("  " + item)
    print("flow workbook updates:", flow_counts)


if __name__ == "__main__":
    main()
