#!/usr/bin/env python3
"""将各主题 textures 文件名统一为与 sanxing 相同的命名规范。"""
from __future__ import annotations

import json
import os
import re
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
TEX = ROOT / "assets" / "resources" / "textures"

RENAMES: list[tuple[str, str, str]] = []


def add(theme: str, old: str, new: str) -> None:
    old = old.replace("\\", "/")
    new = new.replace("\\", "/")
    if not old.endswith(".png"):
        old += ".png"
    if not new.endswith(".png"):
        new += ".png"
    RENAMES.append((theme, old, new))


# ── silu ──
for src, dst in [
    ("素材切图_画板 1", "bg"),
    ("素材切图-03", "anniu_03"),
    ("素材切图-05", "anniu_06"),
    ("1倍速", "anniu_08"),
    ("2倍速", "anniu_10"),
    ("4倍速", "anniu_12"),
    ("素材切图-09", "anniu_17"),
    ("素材切图-10", "anniu_19"),
    ("声音", "anniu_22"),
    ("声音关闭", "anniu_21"),
    ("素材切图-23", "kuai11"),
    ("Decor23", "kuai11"),
]:
    add("silu", src, dst)
for i, src in enumerate(["机器人", "机器人2", "机器人3", "机器人4"], start=1):
    add("silu", f"skin/跳背面/{src}", f"skin/跳背面/{i}")

# ── snow ──
add("snow", "Prop_kuai2", "Prop_kuai1")
add("snow", "nProp_kuai2", "nProp_kuai1")

# ── numMan ──
for i in range(1, 5):
    add("numMan", f"skin/待机/待机{i}", f"skin/待机正面/{i}")
for i in range(1, 3):
    add("numMan", f"skin/待机背面/待机{i}", f"skin/待机背面/{i}")
for i in range(1, 5):
    add("numMan", f"skin/走/走{i}", f"skin/走/{i}")
    add("numMan", f"skin/走背面/走背面{i}", f"skin/走背面/{i}")
add("numMan", "skin/跳/跳1", "skin/跳/1")
add("numMan", "skin/跳/跳2", "skin/跳/2")
add("numMan", "skin/跳/跳4", "skin/跳/3")
add("numMan", "skin/跳/跳5", "skin/跳/4")
add("numMan", "skin/跳背面/跳背面1", "skin/跳背面/1")
add("numMan", "skin/跳背面/跳背面2", "skin/跳背面/2")
add("numMan", "skin/跳背面/跳背面4", "skin/跳背面/3")
add("numMan", "skin/跳背面/跳背面5", "skin/跳背面/4")
add("numMan", "Prop_kuai", "Prop_kuai1")
add("numMan", "nProp_kuai", "nProp_kuai1")

# ── redArmy ──
for src, dst in [
    ("小游戏素材红色_03", "kuai11"),
    ("redarmy02", "anniu_03"),
    ("redarmy09", "anniu_06"),
    ("redarmy11", "anniu_08"),
    ("redarmy13", "anniu_10"),
    ("redarmy15", "anniu_12"),
    ("redarmy20", "anniu_17"),
    ("redarmy29", "anniu_19"),
    ("redarmy35", "anniu_22"),
    ("redarmy36", "anniu_21"),
    ("Prop_star", "Prop_kuai1"),
    ("nProp_star", "nProp_kuai1"),
    ("redarmyship_F", "redArmyShip_F"),
    ("redarmyship_B", "redArmyShip_B"),
]:
    add("redArmy", src, dst)
for i in range(1, 3):
    add("redArmy", f"skin/待机/小红军待机{i}", f"skin/待机正面/{i}")
    add("redArmy", f"skin/待机背面/小红军待机{i}", f"skin/待机背面/{i}")
for i in range(1, 4):
    add("redArmy", f"skin/走/小红军走{i}", f"skin/走/{i}")
    add("redArmy", f"skin/走背面/小红军走{i}", f"skin/走背面/{i}")
    add("redArmy", f"skin/跳/小红军跳{i}", f"skin/跳/{i}")
    add("redArmy", f"skin/跳背面/小红军跳背面{i}", f"skin/跳背面/{i}")

# ── chinese ──
for src, dst in [
    ("素材切图2_画板 1", "bg"),
    ("素材切图2-23", "kuai11"),
    ("素材切图2-02", "anniu_03"),
    ("素材切图2-03", "anniu_06"),
    ("素材切图2-06", "anniu_08"),
    ("素材切图2-07", "anniu_10"),
    ("素材切图2-08", "anniu_12"),
    ("素材切图2-09", "anniu_17"),
    ("素材切图2-10", "anniu_19"),
    ("素材切图2-11", "anniu_22"),
    ("素材切图2-12", "anniu_21"),
    ("Prop_Dumpling", "Prop_kuai1"),
    ("nProp_Dumpling", "nProp_kuai1"),
    ("yun_F", "chineseShip_F"),
    ("yun_B", "chineseShip_B"),
]:
    add("chinese", src, dst)
add("chinese", "skin/待机/机器人", "skin/待机正面/1")
add("chinese", "skin/待机/机器人-1", "skin/待机正面/2")
add("chinese", "skin/待机背面/机器人", "skin/待机背面/1")
add("chinese", "skin/待机背面/机器人-1", "skin/待机背面/2")
add("chinese", "skin/走/机器人", "skin/走/1")
add("chinese", "skin/走/机器人-1", "skin/走/2")
add("chinese", "skin/走/机器人-2", "skin/走/3")
add("chinese", "skin/走背面/机器人", "skin/走背面/1")
add("chinese", "skin/走背面/机器人-1", "skin/走背面/2")
add("chinese", "skin/走背面/机器人-2", "skin/走背面/3")
add("chinese", "skin/跳/机器人", "skin/跳/1")
add("chinese", "skin/跳/机器人-1", "skin/跳/2")
add("chinese", "skin/跳/机器人-2", "skin/跳/3")
add("chinese", "skin/跳背面/机器人", "skin/跳背面/1")
add("chinese", "skin/跳背面/机器人-1", "skin/跳背面/2")
add("chinese", "skin/跳背面/机器人-2", "skin/跳背面/3")


def _norm_rel(p: str) -> str:
    p = p.replace("\\", "/")
    return p[:-4] if p.endswith(".png") else p


def build_rename_lookup() -> dict[tuple[str, str], str]:
    lookup: dict[tuple[str, str], str] = {}
    for theme, old, new in RENAMES:
        old_n = _norm_rel(old)
        new_n = _norm_rel(new)
        lookup[(theme, Path(old_n).name)] = new_n
        lookup[(theme, old_n)] = new_n
    return lookup


def move_pair(src: Path, dst: Path, *, skip_if_dst_exists: bool = False) -> bool:
    if not src.exists():
        return False
    dst.parent.mkdir(parents=True, exist_ok=True)
    if dst.exists():
        if skip_if_dst_exists:
            legacy = dst.parent / "_legacy" / "duplicates" / src.name
            legacy.parent.mkdir(parents=True, exist_ok=True)
            return move_pair(src, legacy)
        return False
    os.rename(src, dst)
    meta_src = Path(str(src) + ".meta")
    meta_dst = Path(str(dst) + ".meta")
    if meta_src.exists():
        if meta_dst.exists():
            meta_dst.unlink()
        os.rename(meta_src, meta_dst)
    return True


def read_meta_display_name(meta_path: Path) -> str | None:
    if not meta_path.exists():
        return None
    try:
        data = json.loads(meta_path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return None
    for sub in (data.get("subMetas") or {}).values():
        name = sub.get("displayName")
        if name:
            return str(name)
    return None


def recover_stages_from_meta() -> int:
    lookup = build_rename_lookup()
    recovered = 0
    for stage in sorted(TEX.rglob("__stage_*.png")):
        theme_key = stage.relative_to(TEX).parts[0]
        display = read_meta_display_name(Path(str(stage) + ".meta"))
        if not display:
            continue
        new_rel = lookup.get((theme_key, display)) or lookup.get((theme_key, _norm_rel(display)))
        if not new_rel:
            continue
        dst = TEX / theme_key / f"{new_rel}.png"
        if move_pair(stage, dst, skip_if_dst_exists=True):
            recovered += 1
            print(f"  recovered {theme_key}/{display} -> {new_rel}.png")
    return recovered


def apply_renames() -> int:
    count = 0
    for theme, old_rel, new_rel in RENAMES:
        src = TEX / theme / old_rel
        dst = TEX / theme / new_rel
        if not src.exists():
            continue
        if dst.exists():
            move_pair(src, src.parent / "_legacy" / "duplicates" / src.name)
            continue
        if move_pair(src, dst):
            count += 1
    return count


def cleanup_empty_dirs(theme_dir: Path) -> None:
    for dirpath, dirnames, filenames in os.walk(theme_dir, topdown=False):
        p = Path(dirpath)
        if p == theme_dir:
            continue
        has_png = any(f.endswith(".png") for f in filenames)
        if not has_png and not dirnames:
            for f in list(p.iterdir()):
                f.unlink()
            meta = Path(str(p) + ".meta")
            if meta.exists():
                meta.unlink()
            p.rmdir()


SANXING_KEEP = {
    "bg", "Baseblock", "JumpBlock", "WallBlock", "kuai11",
    "Prop_kuai1", "nProp_kuai1",
    "anniu_03", "anniu_06", "anniu_08", "anniu_10", "anniu_12",
    "anniu_17", "anniu_19", "anniu_21", "anniu_22",
}

THEME_SHIP = {
    "silu": ("siluShip_F", "siluShip_B"),
    "snow": ("snowShip_F", "snowShip_B"),
    "numMan": ("numManShip_F", "numManShip_B"),
    "redArmy": ("redArmyShip_F", "redArmyShip_B"),
    "chinese": ("chineseShip_F", "chineseShip_B"),
    "sanxing": ("sanxingShip_F", "sanxingShip_B"),
}


def move_legacy(theme: str, keep_names: set[str]) -> None:
    theme_dir = TEX / theme
    legacy = theme_dir / "_legacy"
    for path in sorted(theme_dir.rglob("*.png")):
        rel = path.relative_to(theme_dir).as_posix()
        if rel.startswith("_legacy/") or rel.startswith("__stage_"):
            continue
        base = rel.replace(".png", "")
        if base in keep_names:
            continue
        if base.startswith("skin/"):
            parts = base.split("/")
            if len(parts) == 3 and parts[1] in {
                "待机正面", "待机背面", "走", "走背面", "跳", "跳背面",
            } and re.fullmatch(r"\d+", parts[2]):
                continue
        dest = legacy / rel
        dest.parent.mkdir(parents=True, exist_ok=True)
        move_pair(path, dest)


def build_path_replacements() -> dict[str, str]:
    pairs = [
        ("textures/silu/素材切图_画板 1", "textures/silu/bg"),
        ("textures/silu/素材切图-23", "textures/silu/kuai11"),
        ("textures/silu/Decor23", "textures/silu/kuai11"),
        ("textures/silu/素材切图-03", "textures/silu/anniu_03"),
        ("textures/silu/素材切图-05", "textures/silu/anniu_06"),
        ("textures/silu/1倍速", "textures/silu/anniu_08"),
        ("textures/silu/2倍速", "textures/silu/anniu_10"),
        ("textures/silu/4倍速", "textures/silu/anniu_12"),
        ("textures/silu/素材切图-09", "textures/silu/anniu_17"),
        ("textures/silu/素材切图-10", "textures/silu/anniu_19"),
        ("textures/silu/声音关闭", "textures/silu/anniu_21"),
        ("textures/silu/声音", "textures/silu/anniu_22"),
        ("textures/snow/Prop_kuai2", "textures/snow/Prop_kuai1"),
        ("textures/snow/nProp_kuai2", "textures/snow/nProp_kuai1"),
        ("textures/numMan/Prop_kuai", "textures/numMan/Prop_kuai1"),
        ("textures/numMan/nProp_kuai", "textures/numMan/nProp_kuai1"),
        ("textures/numMan/skin/待机/待机1", "textures/numMan/skin/待机正面/1"),
        ("textures/numMan/skin/待机背面/待机1", "textures/numMan/skin/待机背面/1"),
        ("textures/redArmy/小游戏素材红色_03", "textures/redArmy/kuai11"),
        ("textures/redArmy/Prop_star", "textures/redArmy/Prop_kuai1"),
        ("textures/redArmy/nProp_star", "textures/redArmy/nProp_kuai1"),
        ("textures/redArmy/redarmyship_F", "textures/redArmy/redArmyShip_F"),
        ("textures/redArmy/redarmyship_B", "textures/redArmy/redArmyShip_B"),
        ("textures/redArmy/redarmy02", "textures/redArmy/anniu_03"),
        ("textures/redArmy/redarmy09", "textures/redArmy/anniu_06"),
        ("textures/redArmy/redarmy11", "textures/redArmy/anniu_08"),
        ("textures/redArmy/redarmy13", "textures/redArmy/anniu_10"),
        ("textures/redArmy/redarmy15", "textures/redArmy/anniu_12"),
        ("textures/redArmy/redarmy20", "textures/redArmy/anniu_17"),
        ("textures/redArmy/redarmy29", "textures/redArmy/anniu_19"),
        ("textures/redArmy/redarmy35", "textures/redArmy/anniu_22"),
        ("textures/redArmy/redarmy36", "textures/redArmy/anniu_21"),
        ("textures/redArmy/skin/待机/小红军待机1", "textures/redArmy/skin/待机正面/1"),
        ("textures/redArmy/skin/待机背面/小红军待机1", "textures/redArmy/skin/待机背面/1"),
        ("textures/chinese/素材切图2_画板 1", "textures/chinese/bg"),
        ("textures/chinese/素材切图2-23", "textures/chinese/kuai11"),
        ("textures/chinese/素材切图2-02", "textures/chinese/anniu_03"),
        ("textures/chinese/素材切图2-03", "textures/chinese/anniu_06"),
        ("textures/chinese/素材切图2-06", "textures/chinese/anniu_08"),
        ("textures/chinese/素材切图2-07", "textures/chinese/anniu_10"),
        ("textures/chinese/素材切图2-08", "textures/chinese/anniu_12"),
        ("textures/chinese/素材切图2-09", "textures/chinese/anniu_17"),
        ("textures/chinese/素材切图2-10", "textures/chinese/anniu_19"),
        ("textures/chinese/素材切图2-11", "textures/chinese/anniu_22"),
        ("textures/chinese/素材切图2-12", "textures/chinese/anniu_21"),
        ("textures/chinese/Prop_Dumpling", "textures/chinese/Prop_kuai1"),
        ("textures/chinese/nProp_Dumpling", "textures/chinese/nProp_kuai1"),
        ("textures/chinese/yun_F", "textures/chinese/chineseShip_F"),
        ("textures/chinese/yun_B", "textures/chinese/chineseShip_B"),
        ("textures/chinese/Panda/待机/机器人", "textures/chinese/skin/待机正面/1"),
        ("textures/chinese/Panda/待机背面/机器人", "textures/chinese/skin/待机背面/1"),
        ("textures/chinese/Panda/走", "textures/chinese/skin/走"),
        ("textures/chinese/Panda/跳", "textures/chinese/skin/跳"),
        ("textures/chinese/Panda/走背面", "textures/chinese/skin/走背面"),
        ("textures/chinese/Panda/跳背面", "textures/chinese/skin/跳背面"),
        ("textures/chinese/Panda/待机", "textures/chinese/skin/待机正面"),
        ('borderDecorKey": "素材切图-23', 'borderDecorKey": "kuai11'),
        ('borderDecorKey": "素材切图2-23', 'borderDecorKey": "kuai11'),
        ('borderDecorKey": "小游戏素材红色_03', 'borderDecorKey": "kuai11'),
    ]
    return dict(pairs)


def patch_text_files(reps: dict[str, str]) -> int:
    count = 0
    for base in [ROOT / "assets", ROOT / "extensions"]:
        if not base.exists():
            continue
        for path in base.rglob("*"):
            if path.suffix not in {".ts", ".json", ".js", ".jsx"}:
                continue
            if "node_modules" in path.parts or "library" in path.parts:
                continue
            text = path.read_text(encoding="utf-8")
            orig = text
            for old, new in sorted(reps.items(), key=lambda x: -len(x[0])):
                text = text.replace(old, new)
            if text != orig:
                path.write_text(text, encoding="utf-8")
                count += 1
                print(f"  patched {path.relative_to(ROOT)}")
    return count


def patch_themes_database() -> None:
    db_path = ROOT / "assets" / "resources" / "theme" / "themes-database.json"
    data = json.loads(db_path.read_text(encoding="utf-8"))
    themes = data["themes"]

    def hud(theme: str) -> dict:
        return {
            "navigation": f"textures/{theme}/anniu_03",
            "revert": f"textures/{theme}/anniu_06",
            "speed1": f"textures/{theme}/anniu_08",
            "speed2": f"textures/{theme}/anniu_10",
            "speed4": f"textures/{theme}/anniu_12",
            "zoomIn": f"textures/{theme}/anniu_17",
            "zoomOut": f"textures/{theme}/anniu_19",
            "audioOn": f"textures/{theme}/anniu_22",
            "audioOff": f"textures/{theme}/anniu_21",
        }

    folder_map = {
        "silu": "silu", "sanxing": "sanxing", "snow": "snow",
        "chinese": "chinese", "numMan": "numMan", "redarmy": "redArmy",
    }

    for key, folder in folder_map.items():
        if key not in themes:
            continue
        t = themes[key]
        ship_f, ship_b = THEME_SHIP[folder]
        t["background"] = f"textures/{folder}/bg"
        t["borderDecorKey"] = "kuai11"
        t["entities"] = {
            "playerFront": f"textures/{folder}/skin/待机正面/1",
            "playerBack": f"textures/{folder}/skin/待机背面/1",
            "vehicleFront": f"textures/{folder}/{ship_f}",
            "vehicleBack": f"textures/{folder}/{ship_b}",
            "prop": f"textures/{folder}/Prop_kuai1",
            "propGround": f"textures/{folder}/nProp_kuai1",
        }
        t["tiles"] = {
            "Baseblock": f"textures/{folder}/Baseblock",
            "JumpBlock": f"textures/{folder}/JumpBlock",
            "WallBlock": f"textures/{folder}/WallBlock",
            "borderDecor": f"textures/{folder}/kuai11",
        }
        existing_hud = t.get("hud", {})
        t["hud"] = {**hud(folder), **{k: v for k, v in existing_hud.items() if k not in hud(folder)}}

    db_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(f"  updated {db_path.relative_to(ROOT)}")


def patch_palettes() -> None:
    idx = ROOT / "assets" / "resources" / "map-tiles" / "palettes" / "_index.json"
    if idx.exists():
        data = json.loads(idx.read_text(encoding="utf-8"))
        for pal in data.get("palettes", []):
            theme = pal.get("theme") or pal.get("id", "")
            folder = {"redarmy": "redArmy"}.get(theme, theme)
            for tile in pal.get("tiles", []):
                tex = tile.get("texture", "")
                if any(x in tex for x in ("素材", "Decor", "小游戏", "kuai11")):
                    tile["texture"] = f"textures/{folder}/kuai11"
        idx.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")

    for name, folder in [("silu", "silu"), ("chinese", "chinese"), ("redarmy", "redArmy")]:
        p = ROOT / "assets" / "resources" / "map-tiles" / "palettes" / f"{name}.json"
        if not p.exists():
            continue
        pal = json.loads(p.read_text(encoding="utf-8"))
        for tile in pal.get("tiles", []):
            tex = tile.get("texture", "")
            if any(x in tex for x in ("素材", "Decor", "小游戏", "kuai11")):
                tile["texture"] = f"textures/{folder}/kuai11"
        p.write_text(json.dumps(pal, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")


def main() -> None:
    print("=== 恢复 __stage__ 贴图 ===")
    n_rec = recover_stages_from_meta()
    print(f"  恢复 {n_rec} 个文件")

    print("=== 重命名贴图 ===")
    n = apply_renames()
    print(f"  完成 {n} 项重命名")

    for theme in ["silu", "snow", "numMan", "redArmy", "chinese"]:
        cleanup_empty_dirs(TEX / theme)
        keep = set(SANXING_KEEP) | set(THEME_SHIP.get(theme, ()))
        move_legacy(theme, keep)

    print("=== 更新配置与代码引用 ===")
    reps = build_path_replacements()
    n_files = patch_text_files(reps)
    patch_themes_database()
    patch_palettes()
    print(f"  共更新 {n_files} 个文本文件")
    print("完成。")


if __name__ == "__main__":
    main()
