"""Construye el workflow ComfyUI de UNA trampa/peligro de escenario de juego (API format).

Trampa o peligro de nivel (pinchos del suelo, sierra giratoria, foso de lava, placa de
presion, columna de llamas, trampa de flechas, charco acido, descarga electrica, sierra
de pared, prensa aplastante, estaca cayendo, brasero...): UN objeto de peligro AISLADO y
centrado, con la perspectiva del juego (lateral o cenital via view), fondo limpio y
uniforme recortable a alpha, estilo consistente para poblar niveles. Es el builder
hermano de comfyui_build_prop_object_workflow / comfyui_build_structure_workflow: mismo
patron (PURO, dict API format) que compone funciones existentes del registry, no reescribe
el grafo.

Diferencia con prop_object (clave para no duplicar): un *prop* es un objeto de decoracion
inerte (barril, cofre, antorcha, planta, roca) que ambienta la escena pero no afecta al
jugador. Un *hazard* es un peligro JUGABLE que daña o mata: el motor le asigna una hitbox
de daño, animacion de disparo/golpe y estado activo/inactivo. Por eso el scaffold empuja
"hazard, trap, dangerous" y el negativo rechaza "character, person, creature" para que
salga el peligro y no un enemigo (un enemigo vivo es comfyui_build_enemy_creature).

Cableado:

    CheckpointLoaderSimple -> [LoraLoader opcional de estilo] -> KSampler
                          -> CLIPTextEncode (prompt scaffold de hazard) ...
                          -> VAEDecode -> [Image Rembg opcional] -> SaveImage

Compone:
  - comfyui_build_txt2img_workflow  -> base txt2img cuadrada
  - comfyui_inject_lora             -> LoRA de estilo opcional (consistencia del set)
  - 'Image Rembg (Remove Background)' (helper local) -> fondo transparente (alpha)

Por que Rembg y NO comfyui_matting_luma_to_alpha por defecto: la mayoria de trampas son
objetos SOLIDOS con silueta definida (pinchos metalicos, sierra, placa de presion, foso);
rembg recorta limpio la silueta dejando alpha, listo para soltar sobre el nivel. La
luma-to-alpha es para translucidos sobre negro (humo/fuego/magia). Si la trampa es
puramente etérea (una columna de llamas, un arco electrico, una nube de gas) y quieres
conservar el falloff translucido, pon transparent=False y recorta con
comfyui_matting_luma_to_alpha (o un on-black + luma) fuera de este builder, igual que en
comfyui_build_status_effect_icon / decal_overlay.

Por que un solo peligro centrado y fondo plano: una trampa se inserta como sprite/objeto
en el motor; el scaffold empuja a "single hazard object, centered, plain background, game
asset" y el negativo por defecto rechaza "multiple objects, character, person, creature,
cropped, out of frame" para mantener UN peligro entero y recortable.

class_types/inputs verificados contra /object_info del servidor (8GB lowvram):
CheckpointLoaderSimple, CLIPTextEncode, EmptyLatentImage, KSampler, VAEDecode, SaveImage,
LoraLoader, 'Image Rembg (Remove Background)' (transparency BOOLEAN).

Funcion pura: sin red, sin I/O. No muta dicts de entrada (copia profunda en el helper de
rembg). Determinista para los mismos argumentos.
"""
from __future__ import annotations

import copy
import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

# Negativo por defecto pensado para trampas/peligros: UN peligro completo y aislado, fondo
# limpio, sin multiples objetos, sin personajes/criaturas vivas (eso es enemy_creature),
# sin texto/marcas ni recortes. No filtra ningun tipo de trampa (pinchos, sierra, lava,
# acido, electricidad... son validos).
_HAZARD_NEGATIVE = (
    "character, person, people, creature, monster, enemy, hero, "
    "multiple objects, cluttered scene, full level, tileset, "
    "blurry, lowres, deformed, bad perspective, "
    "text, watermark, signature, logo, photo, photorealistic, "
    "cropped, cut off, out of frame, jpeg artifacts"
)


def _inject_rembg(workflow: dict, model: str) -> dict:
    """Inserta 'Image Rembg (Remove Background)' (transparency=True) entre VAEDecode y SaveImage.

    Mismo helper que usan comfyui_build_prop_object_workflow / structure: el nodo recorta
    la silueta del peligro dejando alpha. Repunta SaveImage.images a la salida del Rembg.
    """
    wf = copy.deepcopy(workflow)
    vaedecode_id = next(
        (nid for nid, n in wf.items() if n.get("class_type") == "VAEDecode"), None
    )
    save_id = next((nid for nid, n in wf.items() if n.get("class_type") == "SaveImage"), None)
    if vaedecode_id is None or save_id is None:
        raise ValueError(
            "comfyui_build_trap_hazard_workflow: no se encontro VAEDecode/SaveImage para Rembg"
        )
    numeric = [int(k) for k in wf.keys() if str(k).isdigit()]
    rembg_id = str((max(numeric) + 1) if numeric else len(wf) + 1)
    wf[rembg_id] = {
        "class_type": "Image Rembg (Remove Background)",
        "inputs": {
            "images": [vaedecode_id, 0],
            "transparency": True,
            "model": model,
            "post_processing": False,
            "only_mask": False,
            "alpha_matting": False,
            "alpha_matting_foreground_threshold": 240,
            "alpha_matting_background_threshold": 10,
            "alpha_matting_erode_size": 10,
            "background_color": "none",
        },
    }
    wf[save_id]["inputs"]["images"] = [rembg_id, 0]
    return wf


def comfyui_build_trap_hazard_workflow(
    hazard: str,
    *,
    view: str = "side",
    style: str = "game hazard trap",
    checkpoint: str = "IMG_dreamshaper_8.safetensors",
    size: int = 512,
    transparent: bool = True,
    seed: int = 0,
    lora: str | None = None,
    lora_strength: float = 1.0,
    rembg_model: str = "u2net",
    negative: str | None = None,
    steps: int = 28,
    cfg: float = 7.0,
    sampler_name: str = "dpmpp_2m",
    scheduler: str = "karras",
    filename_prefix: str = "trap_hazard",
) -> dict:
    """Construye el dict (API format) del workflow de una trampa/peligro de escenario.

    Args:
        hazard: descripcion del peligro (ej. "spiked floor trap", "spinning saw blade",
            "lava pit", "pressure plate", "flame jet", "arrow trap", "acid pool",
            "electric trap", "falling spike", "swinging blade", "bear trap"). Se inserta
            en un prompt scaffold de trampa. No puede estar vacio.
        view: perspectiva del juego con la que se muestra el peligro. Se inserta como
            "{view} view" en el prompt (ej. "side", "top-down", "isometric", "front",
            "3/4"). Por defecto "side" (la mas comun para trampas de plataformas).
            Vacio/None -> sin clausula de vista. keyword-only.
        style: descriptor de estilo que mantiene consistentes los peligros del set (ej.
            "game hazard trap", "pixel art hazard", "low poly stylized trap", "cartoon
            danger", "dark fantasy trap"). Pasa el MISMO view + style + checkpoint +
            (lora) a todas las trampas del nivel para coherencia visual. keyword-only.
        checkpoint: checkpoint del servidor. 'IMG_dreamshaper_8.safetensors' (SD1.5, holgado
            en 8GB lowvram) por defecto; 'IMG_juggernaut_xl_v11.safetensors' para SDXL (mas
            VRAM, subir size a 768/1024). keyword-only.
        size: lado del cuadrado en px (width = height = size). 512 SD1.5 por defecto.
            keyword-only.
        transparent: si True inyecta Image Rembg y el PNG sale con alpha (fondo recortado,
            listo para soltar sobre el nivel). Si False deja el peligro opaco sobre fondo
            plano, recortable luego por el caller/pipeline (recomendado para hazards
            etereos: llamas/electricidad/gas + luma-to-alpha). keyword-only.
        seed: semilla del KSampler. Misma seed + mismo hazard/view/style -> misma trampa.
            keyword-only.
        lora: LoRA de estilo opcional en models/loras (ej.
            'SD15_pixel_art.safetensors', 'SDXL_stylized_game_assets.safetensors'). None =
            sin LoRA. keyword-only.
        lora_strength: fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0].
            keyword-only.
        rembg_model: modelo Rembg ('u2net' general, 'isnet-anime' para anime). Solo se usa
            si transparent=True. keyword-only.
        negative: prompt negativo. None usa el negativo por defecto pensado para trampas
            (un peligro aislado y entero, sin personajes/criaturas vivas, sin multiples
            objetos, fondo limpio, sin texto/recorte). keyword-only.
        steps, cfg, sampler_name, scheduler, filename_prefix: parametros de generacion.
            keyword-only.

    Returns:
        dict en API format listo para comfyui_submit_workflow: txt2img base cuadrada con
        prompt scaffold de trampa ('{hazard}, {view} view, {style}, single hazard object,
        centered, plain background, game asset, ...') + LoRA de estilo opcional + Image
        Rembg (si transparent). Es UNA trampa; para poblar un nivel -> llamar por cada
        hazard con el mismo view/style/checkpoint/(lora). Montar el set con
        comfyui_build_grid si se quiere un contact-sheet de los peligros.

    Raises:
        ValueError: si hazard esta vacio, o si la base no tiene VAEDecode/SaveImage donde
            inyectar el Rembg (propagado por el helper).
    """
    from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow

    if not hazard or not hazard.strip():
        raise ValueError(
            "comfyui_build_trap_hazard_workflow: 'hazard' no puede estar vacio"
        )

    hazard = hazard.strip()
    view = (view or "").strip()
    lora_strength = max(0.0, min(2.0, float(lora_strength)))
    neg = _HAZARD_NEGATIVE if negative is None else negative

    # Prompt scaffold de hazard: UN peligro aislado (no un personaje), centrado, fondo
    # plano, listo como asset de juego recortable. El "{view} view" fija la perspectiva
    # del nivel. "hazard, trap, dangerous" empuja al objeto de peligro, no a un enemigo.
    view_clause = f"{view} view, " if view else ""
    positive = (
        f"{hazard}, {view_clause}{style}, single hazard object, "
        "trap, dangerous, centered, plain background, game asset, "
        "high detail"
    )

    wf = comfyui_build_txt2img_workflow(
        checkpoint,
        positive,
        neg,
        steps=steps,
        cfg=cfg,
        width=size,
        height=size,
        seed=seed,
        sampler_name=sampler_name,
        scheduler=scheduler,
        filename_prefix=filename_prefix,
    )

    if lora:
        from ml.comfyui_inject_lora import comfyui_inject_lora

        wf = comfyui_inject_lora(
            wf, lora, strength_model=lora_strength, strength_clip=lora_strength
        )

    if transparent:
        wf = _inject_rembg(wf, rembg_model)

    return wf


if __name__ == "__main__":
    import json

    wf = comfyui_build_trap_hazard_workflow(
        "spiked floor trap",
        view="side",
        style="game hazard trap",
        transparent=True,
        seed=7,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
