#!/usr/bin/env python3
"""
从 Cocos 工程导出 levels-database.json（权威数据源）。

地图 / 主题：level-prefabs/Level{id}.prefab 上的 LevelMapData（groundJson / borderJson / theme）
实体 spawn / boundary：保留 assets/level-data/levels-database.json 中已有条目（关卡编辑器维护）

Unity 主站仅作 ID 对照参考，不参与本导出。
"""
from __future__ import annotations

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

from level_id import LEVEL_ID_BASE, level_db_path, normalize_level_id, prefab_resource_path

LEVEL_MAP_TYPE = "d4e5fanuMlNDh8qO0xdbn+K"
PREFAB_NAME_RE = re.compile(r"^Level(\d+)\.prefab$", re.I)


def parse_prefab_map_data(prefab_path: Path) -> dict | None:
    try:
        objs = json.loads(prefab_path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as e:
        print(f"warn: skip {prefab_path.name}: {e}", file=sys.stderr)
        return None
    if not isinstance(objs, list):
        return None
    for obj in objs:
        if not isinstance(obj, dict):
            continue
        if obj.get("__type__") != LEVEL_MAP_TYPE and "groundJson" not in obj:
            continue
        if "groundJson" not in obj and "levelID" not in obj:
            continue
        level_id = int(obj.get("levelID") or 0)
        ground = {}
        border = {}
        try:
            ground = json.loads(obj.get("groundJson") or "{}")
        except json.JSONDecodeError:
            pass
        try:
            border = json.loads(obj.get("borderJson") or "{}")
        except json.JSONDecodeError:
            pass
        theme = str(obj.get("theme") or "").strip() or None
        return {
            "levelID": level_id,
            "ground": ground if isinstance(ground, dict) else {},
            "border": border if isinstance(border, dict) else {},
            "theme": theme,
        }
    return None


def load_existing_db(path: Path) -> dict:
    if not path.is_file():
        return {"levels": {}}
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return {"levels": {}}


def merge_entry(level_id: int, map_data: dict | None, prev: dict | None) -> dict:
    ext_id = normalize_level_id(level_id)
    out: dict = {
        "levelID": ext_id,
        "boundary": (prev or {}).get("boundary") or {"x": 20, "y": 20},
        "spawns": list((prev or {}).get("spawns") or []),
        "cocosPrefab": prefab_resource_path(ext_id),
    }
    if prev and prev.get("unityPrefab"):
        out["unityPrefab"] = prev["unityPrefab"]
    if map_data:
        if map_data.get("ground"):
            out["ground"] = map_data["ground"]
        if map_data.get("border"):
            out["border"] = map_data["border"]
        if map_data.get("theme"):
            out["theme"] = map_data["theme"]
        if map_data.get("levelID") and map_data["levelID"] > 0:
            out["levelID"] = normalize_level_id(map_data["levelID"])
    elif prev:
        for k in ("ground", "border", "theme", "entityTextures"):
            if prev.get(k) is not None:
                out[k] = prev[k]
    return out


def export_db(project: Path, output: Path, prefab_dir: Path, level_id: int | None = None) -> dict:
    existing = load_existing_db(output)
    prev_levels = existing.get("levels") or {}

    levels: dict[str, dict] = dict(prev_levels)
    prefab_files = sorted(prefab_dir.glob("Level*.prefab"), key=lambda p: p.name)
    if level_id is not None:
        target = normalize_level_id(level_id)
        prefab_files = [
            p for p in prefab_files
            if (m := PREFAB_NAME_RE.match(p.name)) and int(m.group(1)) == target
        ]
    if not prefab_files and level_id is None:
        print(f"error: no prefabs in {prefab_dir}", file=sys.stderr)
        sys.exit(1)
    if level_id is not None and not prefab_files:
        print(f"error: prefab Level{normalize_level_id(level_id)} not found in {prefab_dir}", file=sys.stderr)
        sys.exit(1)

    for pf in prefab_files:
        m = PREFAB_NAME_RE.match(pf.name)
        if not m:
            continue
        lid = int(m.group(1))
        map_data = parse_prefab_map_data(pf)
        prev = prev_levels.get(str(lid)) or prev_levels.get(str(normalize_level_id(lid)))
        levels[str(normalize_level_id(lid))] = merge_entry(lid, map_data, prev)

    if level_id is None:
        # 保留仅有 DB 条目、尚无预制体的关卡（编辑器先写 spawns）
        for key, prev in prev_levels.items():
            if key not in levels:
                try:
                    lid = int(key)
                except ValueError:
                    continue
                levels[key] = merge_entry(lid, None, prev)

    prev_stats = existing.get("stats") or {}
    total_with_maps = sum(
        1 for v in levels.values() if (v.get("ground") or v.get("border"))
    )
    return {
        "version": 2,
        "generatedAt": datetime.now(timezone.utc).isoformat(),
        "source": "Cocos level-prefabs LevelMapData + levels-database spawns",
        "levelIdBase": LEVEL_ID_BASE,
        "stats": {
            "total": len(levels),
            "withPrefabTilemap": total_with_maps,
            "withBoundaryRing": prev_stats.get("withBoundaryRing", 0),
        },
        "levels": dict(sorted(levels.items(), key=lambda kv: int(kv[0]))),
    }


def resolve_prefab_dir(project: Path) -> Path:
    """迁移后预制体在 bundle-level-prefabs/level-prefabs，否则仍在 resources 下。"""
    bundle = project / "assets/bundle-level-prefabs/level-prefabs"
    legacy = project / "assets/resources/level-prefabs"
    if bundle.is_dir():
        return bundle
    return legacy


def main():
    ap = argparse.ArgumentParser(description="从 Cocos 预制体导出 levels-database.json")
    ap.add_argument(
        "--project",
        default=str(Path(__file__).resolve().parent.parent),
        help="Cocos 工程根目录",
    )
    ap.add_argument(
        "--output",
        default="",
        help="输出 JSON（默认 assets/level-data/levels-database.json）",
    )
    ap.add_argument(
        "--prefab-dir",
        default="",
        help="关卡预制体目录（默认自动检测 bundle-level-prefabs 或 resources）",
    )
    ap.add_argument(
        "--level-id",
        type=int,
        default=None,
        help="仅导出/合并指定关卡（增量更新 levels-database.json）",
    )
    args = ap.parse_args()

    project = Path(args.project)
    output = Path(args.output) if args.output else level_db_path(project)
    prefab_dir = Path(args.prefab_dir) if args.prefab_dir else resolve_prefab_dir(project)

    data = export_db(project, output, prefab_dir, args.level_id)
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"Exported {data['stats']['total']} levels -> {output}")
    print(f"  with prefab tilemap: {data['stats']['withPrefabTilemap']}")


if __name__ == "__main__":
    main()
