#!/usr/bin/env python3
"""
将 level-prefabs/Level{N}.prefab 重命名为 Level{91600+N}.prefab，并同步 JSON 数据库。

与 Unity / 主站 config.js BEGINNING_REAL_LVID=91601 对齐：游戏 ID 从 91601 起（首关 Level91601）。

用法:
  python3 tools/renumber-level-prefabs.py --dry-run
  python3 tools/renumber-level-prefabs.py
"""
from __future__ import annotations

import argparse
import json
import re
from datetime import datetime, timezone
from pathlib import Path

from level_id import (
    LEVEL_ID_BASE,
    internal_level_index as to_internal_id,
    prefab_resource_path,
    touch_database,
)


def to_external_id(internal: int) -> int:
    return LEVEL_ID_BASE + internal

PREFAB_DIR_NAME = "level-prefabs"
LEVEL_RE = re.compile(r"^Level(\d+)\.prefab$", re.I)


def rename_prefabs(prefab_dir: Path, dry_run: bool) -> dict[int, int]:
    """internal -> external mapping for renamed files."""
    mapping: dict[int, int] = {}
    files = sorted(
        [p for p in prefab_dir.glob("Level*.prefab") if LEVEL_RE.match(p.name)],
        key=lambda p: int(LEVEL_RE.match(p.name).group(1)),  # type: ignore
        reverse=True,
    )
    for src in files:
        m = LEVEL_RE.match(src.name)
        if not m:
            continue
        internal = int(m.group(1))
        if internal > LEVEL_ID_BASE:
            continue
        external = to_external_id(internal)
        dst = prefab_dir / f"Level{external}.prefab"
        meta_src = Path(str(src) + ".meta")
        meta_dst = Path(str(dst) + ".meta")
        mapping[internal] = external
        if dry_run:
            if internal <= 3 or internal in (60, 80, 4188):
                print(f"  {src.name} -> {dst.name}")
            continue
        if dst.exists():
            raise SystemExit(f"目标已存在，中止: {dst}")
        src.rename(dst)
        if meta_src.is_file():
            meta_src.rename(meta_dst)
    return mapping


def migrate_levels_database(db_path: Path, mapping: dict[int, int], dry_run: bool) -> None:
    if not db_path.is_file():
        print(f"skip database (not found): {db_path}")
        return
    data = json.loads(db_path.read_text(encoding="utf-8"))
    old_levels = data.get("levels") or {}
    new_levels: dict[str, dict] = {}
    for key, cfg in old_levels.items():
        try:
            old_id = int(key)
        except ValueError:
            old_id = int(cfg.get("levelID", key))
        internal = to_internal_id(old_id) if old_id > LEVEL_ID_BASE else old_id
        external = mapping.get(internal, to_external_id(internal))
        entry = dict(cfg)
        entry["levelID"] = external
        unity_prefab = entry.get("unityPrefab", "")
        if isinstance(unity_prefab, str) and unity_prefab:
            entry["unityPrefab"] = re.sub(
                r"Level\d+\.prefab$",
                f"Level{external}.prefab",
                unity_prefab.replace("\\", "/"),
                flags=re.I,
            )
        entry["cocosPrefab"] = prefab_resource_path(external)
        new_levels[str(external)] = entry
    data["levels"] = new_levels
    if dry_run:
        print(f"database: {len(old_levels)} -> {len(new_levels)} keys:", sorted(new_levels.keys()))
        return
    db_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
    touch_database(db_path)
    print(f"updated {db_path}")


def rebuild_prefab_index(prefab_dir: Path, index_path: Path, dry_run: bool) -> None:
    index: dict[str, str] = {}
    for p in sorted(
        prefab_dir.glob("Level*.prefab"),
        key=lambda x: int(LEVEL_RE.match(x.name).group(1)),  # type: ignore
    ):
        m = LEVEL_RE.match(p.name)
        if not m:
            continue
        lid = int(m.group(1))
        index[str(lid)] = f"{PREFAB_DIR_NAME}/Level{lid}"
    if dry_run:
        print(f"index: {len(index)} entries")
        return
    index_path.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(f"updated {index_path} ({len(index)} entries)")


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--project", type=Path, default=Path(__file__).resolve().parents[1])
    ap.add_argument("--dry-run", action="store_true")
    args = ap.parse_args()
    project = args.project
    prefab_dir = project / "assets" / "resources" / PREFAB_DIR_NAME
    db_path = project / "assets" / "level-data" / "levels-database.json"
    index_path = project / "tools" / "level-prefab-index.json"

    if not prefab_dir.is_dir():
        raise SystemExit(f"prefab dir not found: {prefab_dir}")

    print(f"{'[dry-run] ' if args.dry_run else ''}Renaming prefabs in {prefab_dir} …")
    mapping = rename_prefabs(prefab_dir, args.dry_run)
    print(f"  mapped {len(mapping)} prefabs (1 -> {to_external_id(1)})")

    migrate_levels_database(db_path, mapping, args.dry_run)
    rebuild_prefab_index(prefab_dir, index_path, args.dry_run)
    print("done.")


if __name__ == "__main__":
    main()
