#!/usr/bin/env python3
"""
FreeExile Monster Archetypes & Exile Character Pipeline Scaffolding Engine (Sprint 2 / R5).
Establishes standardized directory layout, pipeline configuration templates, and UV manifests for:
  - 10 Monster Archetypes (8-directional kinematics: S, SW, W, NW, N, NE, E, SE)
  - 6 Exile Character classes with Song Binh (Dual-Weapon Auto-Swap) weapon pairs.
"""

from __future__ import annotations
import argparse
import json
from pathlib import Path
import sys
from typing import Dict, Any, Optional

PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent


def next_power_of_two(n: int) -> int:
    """Calculates the smallest power of two greater than or equal to n."""
    if n <= 0:
        return 1
    return 1 << (n - 1).bit_length()


CANONICAL_DIRECTIONS = ["S", "SW", "W", "NW", "N", "NE", "E", "SE"]

DEFAULT_MONSTER_ACTIONS = {
    "idle": {"frameCount": 4, "fps": 6, "loop": True},
    "walk": {"frameCount": 6, "fps": 10, "loop": True},
    "attack": {"frameCount": 6, "fps": 14, "loop": False, "hit_frame": 2},
    "hurt": {"frameCount": 3, "fps": 12, "loop": False},
    "death": {"frameCount": 5, "fps": 10, "loop": False},
    "stun": {"frameCount": 3, "fps": 8, "loop": True},
}

MONSTER_ARCHETYPES = [
    {"genus": "mob_risen_skeleton", "name_vi": "Khô Lâu Tử Sĩ", "name_en": "Risen Skeleton Warrior", "element": "KIM", "category": "UNDEAD", "frame_width": 160, "frame_height": 160, "base_stride_meters": 2.2, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_feral_hellhound", "name_vi": "Hoang Vực Cuồng Khuyển", "name_en": "Feral Hellhound", "element": "HOA", "category": "BEAST", "frame_width": 160, "frame_height": 160, "base_stride_meters": 3.2, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_blood_crawler", "name_vi": "Huyết Chu Bọ Cạp", "name_en": "Blood Crawler Arachnid", "element": "MOC", "category": "INSECT", "frame_width": 160, "frame_height": 160, "base_stride_meters": 2.4, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_bramble_treant", "name_vi": "Man Tộc Rễ Gai", "name_en": "Bramble Treant", "element": "MOC", "category": "PLANT", "frame_width": 160, "frame_height": 160, "base_stride_meters": 1.8, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_corrupted_raptor", "name_vi": "Dị Biến Ác Điểu", "name_en": "Corrupted Raptor", "element": "HOA", "category": "AVIAN", "frame_width": 160, "frame_height": 160, "base_stride_meters": 3.6, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_flesh_abomination", "name_vi": "Thi Quỷ Chắp Vá", "name_en": "Flesh Abomination", "element": "THO", "category": "CONSTRUCT", "frame_width": 160, "frame_height": 160, "base_stride_meters": 1.6, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_ironhide_behemoth", "name_vi": "Cương Ngưu Thiết Bì", "name_en": "Ironhide Behemoth", "element": "KIM", "category": "BEAST", "frame_width": 160, "frame_height": 160, "base_stride_meters": 2.0, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_primal_cannibal", "name_vi": "Man Tộc Ăn Thịt Người", "name_en": "Primal Cannibal Butcher", "element": "HOA", "category": "HUMANOID", "frame_width": 160, "frame_height": 160, "base_stride_meters": 2.6, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_shadow_wraith", "name_vi": "U Hồn Tà Ma", "name_en": "Shadow Wraith", "element": "THUY", "category": "SPIRIT", "frame_width": 160, "frame_height": 160, "base_stride_meters": 3.0, "actions": DEFAULT_MONSTER_ACTIONS},
    {"genus": "mob_tomb_lord", "name_vi": "Lăng Mộ Vương", "name_en": "Ancient Tomb Lord", "element": "THO", "category": "BOSS", "frame_width": 160, "frame_height": 160, "base_stride_meters": 2.2, "actions": DEFAULT_MONSTER_ACTIONS},
]

DEFAULT_EXILE_ACTIONS = {
    "idle": {"frameCount": 4, "fps": 6, "loop": True},
    "run": {"frameCount": 8, "fps": 12, "loop": True},
    "attack_slash": {"frameCount": 6, "fps": 15, "loop": False, "hit_frame": 2},
    "attack_thrust": {"frameCount": 6, "fps": 15, "loop": False, "hit_frame": 2},
    "attack_slam": {"frameCount": 6, "fps": 15, "loop": False, "hit_frame": 3},
    "attack_shoot": {"frameCount": 6, "fps": 15, "loop": False, "hit_frame": 2},
    "skill_whirlwind": {"frameCount": 6, "fps": 18, "loop": True},
    "dodge": {"frameCount": 6, "fps": 20, "loop": False, "iframe_duration_ms": 250},
    "hurt": {"frameCount": 3, "fps": 12, "loop": False},
    "death": {"frameCount": 5, "fps": 10, "loop": False},
}

EXILE_CHARACTERS = [
    {"class_id": "char_sword_master", "name_vi": "Kiếm Tu (Nam)", "name_en": "Sword Master", "element": "KIM", "gender": "MALE", "frame_width": 160, "frame_height": 192, "song_binh": {"primary": {"weapon_type": "SWORD", "name": "Azure Wind Sword", "category": "1H_MELEE"}, "secondary": {"weapon_type": "BLADE", "name": "Infernal Blade", "category": "1H_MELEE"}, "auto_swap_delay_ms": 0}, "actions": DEFAULT_EXILE_ACTIONS},
    {"class_id": "char_sword_maiden", "name_vi": "Kiếm Nữ (Nữ)", "name_en": "Sword Maiden", "element": "KIM", "gender": "FEMALE", "frame_width": 160, "frame_height": 192, "song_binh": {"primary": {"weapon_type": "SWORD", "name": "Azure Wind Sword", "category": "1H_MELEE"}, "secondary": {"weapon_type": "BLADE", "name": "Infernal Blade", "category": "1H_MELEE"}, "auto_swap_delay_ms": 0}, "actions": DEFAULT_EXILE_ACTIONS},
    {"class_id": "char_feral_berserker", "name_vi": "Cuồng Đao (Nam)", "name_en": "Feral Berserker", "element": "HOA", "gender": "MALE", "frame_width": 160, "frame_height": 192, "song_binh": {"primary": {"weapon_type": "BLADE_2H", "name": "Infernal Greatblade", "category": "2H_MELEE"}, "secondary": {"weapon_type": "SWORD", "name": "Azure Wind Sword", "category": "1H_MELEE"}, "auto_swap_delay_ms": 0}, "actions": DEFAULT_EXILE_ACTIONS},
    {"class_id": "char_wild_archer", "name_vi": "Man Xạ Nữ (Nữ)", "name_en": "Wild Archer", "element": "MOC", "gender": "FEMALE", "frame_width": 160, "frame_height": 192, "song_binh": {"primary": {"weapon_type": "RANGED_BOW", "name": "Feral Bone Bow", "category": "2H_RANGED"}, "secondary": {"weapon_type": "DAGGER", "name": "Twin Fang Daggers", "category": "DUAL_MELEE"}, "auto_swap_delay_ms": 0}, "actions": DEFAULT_EXILE_ACTIONS},
    {"class_id": "char_glacial_lancer", "name_vi": "Thần Thương (Nam)", "name_en": "Glacial Lancer", "element": "THUY", "gender": "MALE", "frame_width": 160, "frame_height": 192, "song_binh": {"primary": {"weapon_type": "STAFF", "name": "Spiked Bone Spear", "category": "2H_POLEARM"}, "secondary": {"weapon_type": "BLADE_2H", "name": "Infernal Greatblade", "category": "2H_MELEE"}, "auto_swap_delay_ms": 0}, "actions": DEFAULT_EXILE_ACTIONS},
    {"class_id": "char_shadow_assassin", "name_vi": "Thích Khách (Nữ)", "name_en": "Shadow Assassin", "element": "THUY", "gender": "FEMALE", "frame_width": 160, "frame_height": 192, "song_binh": {"primary": {"weapon_type": "DAGGER", "name": "Twin Fang Daggers", "category": "DUAL_MELEE"}, "secondary": {"weapon_type": "RANGED_BOW", "name": "Feral Bone Bow", "category": "2H_RANGED"}, "auto_swap_delay_ms": 0}, "actions": DEFAULT_EXILE_ACTIONS},
]


def scaffold_monsters(base_dir: Optional[Path] = None) -> Dict[str, Path]:
    """Sets up directories and templates for all 10 Monster Archetypes."""
    root = base_dir or (PROJECT_ROOT / "client" / "cocos" / "assets" / "resources" / "monsters" / "archetypes")
    results: Dict[str, Path] = {}

    for archetype in MONSTER_ARCHETYPES:
        genus = archetype["genus"]
        dest_dir = root / genus
        dest_dir.mkdir(parents=True, exist_ok=True)

        total_rows = len(archetype["actions"]) * len(CANONICAL_DIRECTIONS)
        total_h = total_rows * archetype["frame_height"]
        total_w = 8 * archetype["frame_width"]
        tex_w = next_power_of_two(total_w)  # 2048
        tex_h = next_power_of_two(total_h)  # 8192

        manifest = {
            "name": f"{genus}_anim_atlas",
            "genus": genus,
            "name_vi": archetype["name_vi"],
            "name_en": archetype["name_en"],
            "element": archetype["element"],
            "category": archetype["category"],
            "textureWidth": tex_w, "textureHeight": tex_h,
            "texture_width": tex_w, "texture_height": tex_h,
            "frameWidth": archetype["frame_width"], "frameHeight": archetype["frame_height"],
            "frame_width": archetype["frame_width"], "frame_height": archetype["frame_height"],
            "cols": 8, "defaultPivot": [0.5, 0.90], "pivot": [0.5, 0.90],
            "directions": CANONICAL_DIRECTIONS,
            "clips": {}, "frames": {}
        }

        row_idx = 0
        for action, act_cfg in archetype["actions"].items():
            for direction in CANONICAL_DIRECTIONS:
                clip_key = f"{action}_{direction}"
                f_count = act_cfg["frameCount"]
                frames_list = []
                for f in range(f_count):
                    f_name = f"{action}_{direction}_{f}"
                    col = f % 8
                    x = col * archetype["frame_width"]
                    y = (row_idx + (f // 8)) * archetype["frame_height"]
                    u0 = x / float(tex_w)
                    v0 = y / float(tex_h)
                    u1 = (x + archetype["frame_width"]) / float(tex_w)
                    v1 = (y + archetype["frame_height"]) / float(tex_h)
                    frame_meta = {
                        "index": f, "frame_id": f_name,
                        "x": x, "y": y,
                        "w": archetype["frame_width"], "h": archetype["frame_height"],
                        "pivot": [0.5, 0.90],
                        "uv": [u0, v0, u1, v1]
                    }
                    frames_list.append(frame_meta)
                    manifest["frames"][f_name] = frame_meta

                manifest["clips"][clip_key] = {
                    "action": action, "direction": direction, "frameCount": f_count,
                    "fps": act_cfg["fps"], "loop": act_cfg.get("loop", True),
                    "hit_frame": act_cfg.get("hit_frame", 2), "frames": frames_list,
                    "frame_keys": [f"{action}_{direction}_{f}" for f in range(f_count)]
                }
                row_idx += 1

        with open(dest_dir / f"{genus}_anim_manifest.json", "w", encoding="utf-8") as f:
            json.dump(manifest, f, indent=2, ensure_ascii=False)

        pipeline_cfg = {
            "genus": genus, "target_resolution": [tex_w, tex_h],
            "frame_resolution": [archetype["frame_width"], archetype["frame_height"]],
            "sobel_normal_intensity": 2.2, "roughness_base": 0.65,
            "dilation_padding_iterations": 8, "hardware_compression": "ASTC_4x4",
            "apple_metal_pixel_format": "MTLPixelFormatASTC_4x4_sRGB",
            "stride_meters": archetype["base_stride_meters"]
        }
        with open(dest_dir / "pipeline_config.json", "w", encoding="utf-8") as f:
            json.dump(pipeline_cfg, f, indent=2, ensure_ascii=False)

        results[genus] = dest_dir
    return results


def scaffold_characters(base_dir: Optional[Path] = None) -> Dict[str, Path]:
    """Sets up directories and templates for all 6 Exile Character classes."""
    root = base_dir or (PROJECT_ROOT / "client" / "cocos" / "assets" / "resources" / "characters")
    results: Dict[str, Path] = {}

    for char_info in EXILE_CHARACTERS:
        class_id = char_info["class_id"]
        dest_dir = root / class_id
        dest_dir.mkdir(parents=True, exist_ok=True)

        total_rows = len(char_info["actions"]) * len(CANONICAL_DIRECTIONS)
        total_h = total_rows * char_info["frame_height"]
        total_w = 8 * char_info["frame_width"]
        tex_w = next_power_of_two(total_w)  # 2048
        tex_h = next_power_of_two(total_h)  # 16384

        manifest = {
            "name": f"{class_id}_anim_atlas",
            "class_id": class_id,
            "name_vi": char_info["name_vi"], "name_en": char_info["name_en"],
            "element": char_info["element"], "gender": char_info["gender"],
            "textureWidth": tex_w, "textureHeight": tex_h,
            "texture_width": tex_w, "texture_height": tex_h,
            "frameWidth": char_info["frame_width"], "frameHeight": char_info["frame_height"],
            "frame_width": char_info["frame_width"], "frame_height": char_info["frame_height"],
            "cols": 8, "defaultPivot": [0.5, 0.90], "pivot": [0.5, 0.90],
            "directions": CANONICAL_DIRECTIONS, "song_binh": char_info["song_binh"],
            "clips": {}, "frames": {}
        }

        row_idx = 0
        for action, act_cfg in char_info["actions"].items():
            for direction in CANONICAL_DIRECTIONS:
                clip_key = f"{action}_{direction}"
                f_count = act_cfg["frameCount"]
                frames_list = []
                for f in range(f_count):
                    f_name = f"{action}_{direction}_{f}"
                    col = f % 8
                    x = col * char_info["frame_width"]
                    y = (row_idx + (f // 8)) * char_info["frame_height"]
                    u0 = x / float(tex_w)
                    v0 = y / float(tex_h)
                    u1 = (x + char_info["frame_width"]) / float(tex_w)
                    v1 = (y + char_info["frame_height"]) / float(tex_h)
                    frame_meta = {
                        "index": f, "frame_id": f_name,
                        "x": x, "y": y,
                        "w": char_info["frame_width"], "h": char_info["frame_height"],
                        "pivot": [0.5, 0.90],
                        "uv": [u0, v0, u1, v1]
                    }
                    frames_list.append(frame_meta)
                    manifest["frames"][f_name] = frame_meta

                manifest["clips"][clip_key] = {
                    "action": action, "direction": direction, "frameCount": f_count,
                    "fps": act_cfg["fps"], "loop": act_cfg.get("loop", True),
                    "hit_frame": act_cfg.get("hit_frame", 2), "frames": frames_list,
                    "frame_keys": [f"{action}_{direction}_{f}" for f in range(f_count)]
                }
                row_idx += 1

        with open(dest_dir / f"{class_id}_anim_manifest.json", "w", encoding="utf-8") as f:
            json.dump(manifest, f, indent=2, ensure_ascii=False)

        with open(dest_dir / "song_binh_config.json", "w", encoding="utf-8") as f:
            json.dump(char_info["song_binh"], f, indent=2, ensure_ascii=False)

        results[class_id] = dest_dir
    return results


def scaffold_all(base_resources_dir: Optional[Path] = None) -> Dict[str, Any]:
    """Generates complete scaffolding for monsters and characters."""
    cocos_res = base_resources_dir or (PROJECT_ROOT / "client" / "cocos" / "assets" / "resources")
    monsters = scaffold_monsters(cocos_res / "monsters" / "archetypes")
    characters = scaffold_characters(cocos_res / "characters")
    return {
        "monsters_scaffolded": len(monsters),
        "monster_paths": {k: str(v) for k, v in monsters.items()},
        "characters_scaffolded": len(characters),
        "character_paths": {k: str(v) for k, v in characters.items()},
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="FreeExile Monster Archetypes & Exile Characters Scaffolding Engine")
    parser.add_argument("--out", help="Optional custom output resources directory")
    args = parser.parse_args()
    out_dir = Path(args.out).resolve() if args.out else None
    summary = scaffold_all(out_dir)
    print(f"[OK] Successfully scaffolded {summary['monsters_scaffolded']} Monster Archetypes.")
    print(f"[OK] Successfully scaffolded {summary['characters_scaffolded']} Exile Character Classes with Song Binh pairs.")
    return 0


if __name__ == "__main__":
    sys.exit(main())
