"""
FreeExile Master Game Asset Producer (100% Dark Fantasy AI Pipeline).
Processes master AI concepts for:
  - Player Character (Savage Primal Exile)
  - 5 Savage Martial Weapons (Obsidian Sword, War Axe, Bone Bow, Spear, Daggers)
  - 6 Barter Bloodstones & Currencies
  - Desolate Deadlands Terrain & Interactive Props (Totem Shrine, Dead Tree)
  - Martial Skills Atlas (6 Abilities)
  - 5 Canonical Feral NPCs (Standees & Avatars)
Generates Tangent-Space Normal Maps and synchronizes directly to Client WebApp.
"""

from __future__ import annotations

import os
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFilter

WORKSPACE_ROOT = Path(__file__).resolve().parent.parent.parent
BRAIN_DIR = Path(r"C:\Users\Admin\.gemini\antigravity\brain\688959f4-7494-467a-82f4-3b8395e5688d")
CLIENT_ASSETS = WORKSPACE_ROOT / "client" / "webapp" / "assets"


def compute_tangent_normal_map(albedo_img: Image.Image, strength: float = 3.0) -> Image.Image:
    """Computes Tangent-Space 3D Normal Map (dx, dy, dz) via vectorized Sobel."""
    gray = albedo_img.convert("L")
    arr = np.array(gray, dtype=np.float32) / 255.0
    padded = np.pad(arr, 1, mode="edge")

    dx = (
        -1.0 * padded[:-2, :-2] + 1.0 * padded[:-2, 2:]
        - 2.0 * padded[1:-1, :-2] + 2.0 * padded[1:-1, 2:]
        - 1.0 * padded[2:, :-2] + 1.0 * padded[2:, 2:]
    ) * strength

    dy = (
        -1.0 * padded[:-2, :-2] - 2.0 * padded[:-2, 1:-1] - 1.0 * padded[:-2, 2:]
        + 1.0 * padded[2:, :-2] + 2.0 * padded[2:, 1:-1] + 1.0 * padded[2:, 2:]
    ) * strength

    dz = np.ones_like(arr)
    len_vec = np.sqrt(dx**2 + dy**2 + dz**2)
    len_vec[len_vec == 0] = 1.0
    nx = dx / len_vec
    ny = dy / len_vec
    nz = dz / len_vec

    r = ((nx * 0.5 + 0.5) * 255).astype(np.uint8)
    g = ((ny * 0.5 + 0.5) * 255).astype(np.uint8)
    b = ((nz * 0.5 + 0.5) * 255).astype(np.uint8)

    alpha = albedo_img.split()[-1] if albedo_img.mode == "RGBA" else Image.new("L", albedo_img.size, 255)
    return Image.merge("RGBA", (Image.fromarray(r), Image.fromarray(g), Image.fromarray(b), alpha))


def apply_soft_alpha_fade(img: Image.Image, margin_pct: float = 0.08) -> Image.Image:
    """Applies smooth alpha falloff along rectangular or circular edges."""
    rgba = img.convert("RGBA")
    w, h = rgba.size
    y, x = np.indices((h, w), dtype=np.float32)
    cx, cy = w / 2.0, h / 2.0
    rx, ry = w * (0.5 - margin_pct), h * (0.5 - margin_pct)

    dist = ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2
    alpha_mask = np.clip(1.0 - (dist - 0.75) / 0.30, 0.0, 1.0)
    alpha_mask = (alpha_mask * 255).astype(np.uint8)
    mask_img = Image.fromarray(alpha_mask, mode="L").filter(ImageFilter.GaussianBlur(radius=3))

    rgba.putalpha(mask_img)
    return rgba


def process_player_character() -> None:
    """Deploys Master Savage Primal Exile Warrior."""
    print("[1/6] Processing Savage Primal Exile Warrior...")
    src = BRAIN_DIR / "savage_primal_warrior_1790696416594.jpg"
    if not src.exists():
        return
    img = Image.open(src).convert("RGBA")
    # Crop warrior focus
    w, h = img.size
    warrior = img.crop((int(w * 0.15), int(h * 0.05), int(w * 0.85), int(h * 0.95)))
    warrior_res = warrior.resize((512, 600), Image.Resampling.LANCZOS)
    warrior_alpha = apply_soft_alpha_fade(warrior_res, margin_pct=0.06)

    # Save to characters/
    char_dir = CLIENT_ASSETS / "characters"
    char_dir.mkdir(parents=True, exist_ok=True)
    out_char = char_dir / "savage_primal_exile.png"
    warrior_alpha.save(out_char, "PNG")
    compute_tangent_normal_map(warrior_alpha, 3.0).save(char_dir / "savage_primal_exile_normal.png", "PNG")
    print(" -> Deployed player character.")


def process_weapons_arsenal() -> None:
    """Extracts 5 brutal weapons from weapons arsenal concept."""
    print("[2/6] Processing Savage Martial Weapons Arsenal...")
    src = BRAIN_DIR / "savage_weapons_arsenal_1790698215289.jpg"
    if not src.exists():
        return
    master = Image.open(src).convert("RGBA")
    w, h = master.size

    # Crop weapons
    crops = [
        ("azure_wind_sword", (int(w * 0.03), int(h * 0.22), int(w * 0.38), int(h * 0.78))),   # Obsidian Broadsword
        ("infernal_blade", (int(w * 0.30), int(h * 0.28), int(w * 0.52), int(h * 0.72))),    # War Axe
        ("wpn_feral_bone_bow", (int(w * 0.45), int(h * 0.24), int(w * 0.74), int(h * 0.78))),# Bone Bow
        ("wpn_spiked_bone_spear", (int(w * 0.60), int(h * 0.25), int(w * 0.85), int(h * 0.86))), # Spiked Spear
        ("wpn_twin_fang_daggers", (int(w * 0.78), int(h * 0.42), int(w * 0.96), int(h * 0.72))), # Bone Daggers
    ]

    wpn_dir = CLIENT_ASSETS / "weapons"
    wpn_dir.mkdir(parents=True, exist_ok=True)

    for key, box in crops:
        cropped = master.crop(box).resize((256, 256), Image.Resampling.LANCZOS)
        fade = apply_soft_alpha_fade(cropped, margin_pct=0.08)
        fade.save(wpn_dir / f"{key}.png", "PNG")
        compute_tangent_normal_map(fade, 3.2).save(wpn_dir / f"{key}_normal.png", "PNG")
        print(f" -> Deployed weapon: {key}")


def process_barter_currencies() -> None:
    """Extracts 6 barter bloodstones and currency relics."""
    print("[3/6] Processing Barter Currencies & Bloodstones...")
    src = BRAIN_DIR / "barter_bloodstones_relics_1790698250498.jpg"
    if not src.exists():
        return
    master = Image.open(src).convert("RGBA")
    w, h = master.size

    # 6 Currencies horizontally laid out on the altar
    # y range roughly: 0.28 to 0.70
    cy_top = int(h * 0.26)
    cy_bot = int(h * 0.72)
    step_w = w / 6.0

    curr_names = [
        "curr_chaos_stone",            # 1. Blood Soul Stone
        "curr_primal_bone_seal",       # 2. Primal Bone Seal
        "curr_nether_bone_nail",       # 3. Nether Bone Nail
        "curr_black_primordial_shard", # 4. Black Primordial Shard
        "curr_corrupted_demon_stone",  # 5. Corrupted Demon Stone
        "void_mirror_relic",           # 6. Mirror of the Forsaken
    ]

    curr_dir = CLIENT_ASSETS / "currencies"
    curr_dir.mkdir(parents=True, exist_ok=True)

    for idx, key in enumerate(curr_names):
        cx1 = int(idx * step_w + step_w * 0.05)
        cx2 = int((idx + 1) * step_w - step_w * 0.05)
        item_crop = master.crop((cx1, cy_top, cx2, cy_bot)).resize((256, 256), Image.Resampling.LANCZOS)
        fade = apply_soft_alpha_fade(item_crop, margin_pct=0.06)
        fade.save(curr_dir / f"{key}.png", "PNG")
        compute_tangent_normal_map(fade, 3.0).save(curr_dir / f"{key}_normal.png", "PNG")
        print(f" -> Deployed currency: {key}")


def process_deadlands_environment() -> None:
    """Deploys Desolate Deadlands Terrain and Interactive Bone Props."""
    print("[4/6] Processing Deadlands Environment & Props...")
    # 1. Terrain Map
    terr_src = BRAIN_DIR / "deadlands_terrain_master_1790698288614.jpg"
    if terr_src.exists():
        terr = Image.open(terr_src).convert("RGB").resize((1024, 1024), Image.Resampling.LANCZOS)
        terr.save(CLIENT_ASSETS / "map" / "tile_stone.png", "PNG")
        compute_tangent_normal_map(terr, 2.5).save(CLIENT_ASSETS / "map" / "tile_stone_normal.png", "PNG")
        print(" -> Deployed Deadlands Master Terrain Map.")

    # 2. Bone Totem Shrine (replaces pavilion)
    totem_src = BRAIN_DIR / "prop_bone_totem_shrine_1790698328462.jpg"
    if totem_src.exists():
        totem = Image.open(totem_src).convert("RGBA").resize((512, 512), Image.Resampling.LANCZOS)
        totem_alpha = apply_soft_alpha_fade(totem, margin_pct=0.06)
        totem_alpha.save(CLIENT_ASSETS / "map" / "prop_roof_pavilion.png", "PNG")
        compute_tangent_normal_map(totem_alpha, 3.2).save(CLIENT_ASSETS / "map" / "prop_roof_pavilion_normal.png", "PNG")
        print(" -> Deployed Bone Totem Shrine prop.")

    # 3. Gnarled Dead Tree (replaces bamboo)
    tree_src = BRAIN_DIR / "prop_dead_tree_gnarled_1790698370216.jpg"
    if tree_src.exists():
        tree = Image.open(tree_src).convert("RGBA").resize((512, 512), Image.Resampling.LANCZOS)
        tree_alpha = apply_soft_alpha_fade(tree, margin_pct=0.05)
        tree_alpha.save(CLIENT_ASSETS / "map" / "prop_bamboo_tree.png", "PNG")
        compute_tangent_normal_map(tree_alpha, 3.2).save(CLIENT_ASSETS / "map" / "prop_bamboo_tree_normal.png", "PNG")
        print(" -> Deployed Gnarled Dead Tree prop.")


def process_martial_skills_atlas() -> None:
    """Deploys Martial Skills Atlas (3x2 Ability Icons)."""
    print("[5/6] Processing Martial Skills Atlas...")
    src = BRAIN_DIR / "savage_skills_icon_sheet_1790698438976.jpg"
    if not src.exists():
        return
    atlas = Image.open(src).convert("RGBA").resize((1024, 1024), Image.Resampling.LANCZOS)
    skill_dir = CLIENT_ASSETS / "skills"
    skill_dir.mkdir(parents=True, exist_ok=True)

    atlas_path = skill_dir / "martial_skills_atlas.png"
    atlas.save(atlas_path, "PNG", optimize=True)
    atlas_norm = compute_tangent_normal_map(atlas, 3.0)
    atlas_norm.save(skill_dir / "martial_skills_atlas_normal.png", "PNG", optimize=True)
    print(" -> Deployed Martial Skills Atlas (1024x1024) & Normal Map.")


def process_feral_npcs() -> None:
    """Extracts 5 Canonical Feral NPCs into standees and avatars."""
    print("[6/6] Processing 5 Canonical Feral NPCs...")
    src = BRAIN_DIR / "npc_feral_council_1790698481982.jpg"
    if not src.exists():
        return
    master = Image.open(src).convert("RGBA")
    w, h = master.size

    # The 5 NPCs in the camp
    npc_boxes = [
        ("npc_doc_co", (int(w * 0.05), int(h * 0.28), int(w * 0.32), int(h * 0.88))),      # Bone crafter
        ("npc_cau_khau", (int(w * 0.25), int(h * 0.42), int(w * 0.50), int(h * 0.82))),    # Shrewd scavenger
        ("npc_huyet_do", (int(w * 0.46), int(h * 0.24), int(w * 0.68), int(h * 0.78))),    # Black market merchant
        ("npc_huyet_vu", (int(w * 0.60), int(h * 0.44), int(w * 0.78), int(h * 0.85))),    # Bone witch priestess
        ("npc_cuong_nhan", (int(w * 0.74), int(h * 0.42), int(w * 0.98), int(h * 0.88))),  # Berserker monk
    ]

    npc_dir = CLIENT_ASSETS / "characters" / "npcs"
    npc_dir.mkdir(parents=True, exist_ok=True)

    for key, box in npc_boxes:
        standee = master.crop(box).resize((320, 480), Image.Resampling.LANCZOS)
        standee_alpha = apply_soft_alpha_fade(standee, margin_pct=0.08)
        standee_alpha.save(npc_dir / f"{key}_standee.png", "PNG")

        # Head / Avatar
        sw, sh = standee.size
        avatar = standee.crop((int(sw * 0.15), int(sh * 0.05), int(sw * 0.85), int(sh * 0.50))).resize((128, 128), Image.Resampling.LANCZOS)
        avatar_alpha = apply_soft_alpha_fade(avatar, margin_pct=0.10)
        avatar_alpha.save(npc_dir / f"{key}_avatar.png", "PNG")
        print(f" -> Deployed NPC: {key} (standee & avatar)")


def produce_all_master_assets() -> None:
    print("=================================================================")
    print(" FREEEXILE: FULL-SCALE MASTER ASSET PRODUCTION PIPELINE (POE2)")
    print("=================================================================")
    process_player_character()
    process_weapons_arsenal()
    process_barter_currencies()
    process_deadlands_environment()
    process_martial_skills_atlas()
    process_feral_npcs()
    print("=================================================================")
    print(" [SUCCESS] ALL GAME ASSET DOMAINS UPGRADED TO AAA GRIMDARK ART! ")
    print("=================================================================")


if __name__ == "__main__":
    produce_all_master_assets()
