"""Construye el workflow ComfyUI de UN decal / overlay con alpha (API format).

Un decal es una textura que se SUPERPONE sobre una superficie del juego con un
blend mode del motor: manchas de sangre, grietas, suciedad, grunge, oxido,
quemaduras, salpicaduras, arañazos, musgo, polvo. La pieza se genera AISLADA sobre
un fondo uniforme (negro por defecto) para poder extraer luego el canal alpha.

La tecnica gamedev correcta para un decal translucido (sangre, humo, salpicadura,
mancha con falloff suave) es generarlo sobre fondo NEGRO y convertir la luminancia
en alpha con `comfyui_matting_luma_to_alpha`: brillante -> opaco, negro ->
transparente. Eso preserva el degradado de los bordes (cosa que un matting binario
tipo rembg destruye). Por eso `on_black=True` es el modo por defecto y el scaffold
empuja "on pure black background": el PNG resultante esta pensado para pasar por
luma-to-alpha en un paso aparte. `on_black=False` (fondo blanco) sirve para decals
oscuros sobre superficies claras o cuando el caller prefiere invertir/recortar de
otra forma.

Es el builder hermano de comfyui_build_seamless_tile_workflow /
comfyui_build_vfx_spritesheet_workflow: mismo patron (PURO, dict API format) que
compone funciones existentes del registry, no reescribe el grafo. A diferencia de
los builders de sprite/prop/item, este NO inyecta Rembg: el matting de un decal es
luma-to-alpha (post-proceso de disco), no un nodo del workflow.

Cableado:

    CheckpointLoaderSimple -> [LoraLoader opcional de estilo grunge] -> KSampler
                          -> CLIPTextEncode (prompt scaffold decal aislado) ...
                          -> VAEDecode -> SaveImage  (decal sobre fondo uniforme)

Compone:
  - comfyui_build_txt2img_workflow  -> base txt2img cuadrada
  - comfyui_inject_lora             -> LoRA de estilo (grunge/sangre/oxido) opcional

Pipeline despues de generar (no en este builder):
  comfyui_submit_workflow -> comfyui_wait_result -> comfyui_fetch_output_image
  -> comfyui_matting_luma_to_alpha (con on_black=True)  -> PNG RGBA listo para el motor

class_types/inputs verificados contra /object_info del servidor (8GB lowvram):
CheckpointLoaderSimple, CLIPTextEncode, EmptyLatentImage, KSampler, VAEDecode,
SaveImage, LoraLoader.

Funcion pura: sin red, sin I/O. Determinista para los mismos argumentos.
"""
from __future__ import annotations

import os
import sys

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

# Negativo comun a cualquier decal: una sola textura plana, plena de detalle, SIN
# objetos 3D, escena, personajes, profundidad ni marco/borde que delaten una
# composicion.
_DECAL_NEGATIVE_COMMON = (
    "3d render, object, scene, landscape, character, person, creature, "
    "depth of field, perspective, vignette, frame, border, drop shadow, "
    "multiple decals, tiled pattern, text, watermark, signature, logo, "
    "blurry, low quality, jpeg artifacts"
)

# Refuerzo de fondo segun on_black: el fondo DEBE quedar PLANO y del color esperado
# para que el matting (luma->alpha sobre negro, o recorte sobre blanco) no recoja
# basura. Sin esto, un style tipo "grunge" arrastra una textura gris de fondo que
# arruina el alpha. Rechaza explicitamente fondos texturizados/de otro color.
_DECAL_BG_NEG_ON_BLACK = (
    "textured background, grunge background, gray background, white background, "
    "busy background, background pattern, noisy background, gradient background"
)
_DECAL_BG_NEG_ON_WHITE = (
    "textured background, grunge background, gray background, black background, "
    "busy background, background pattern, noisy background, gradient background"
)


def comfyui_build_decal_overlay_workflow(
    decal: str,
    *,
    on_black: bool = True,
    style: str = "grunge decal, high detail",
    checkpoint: str = "IMG_dreamshaper_8.safetensors",
    size: int = 512,
    seed: int = 0,
    lora: str | None = None,
    lora_strength: float = 1.0,
    negative: str | None = None,
    steps: int = 28,
    cfg: float = 7.0,
    sampler_name: str = "dpmpp_2m",
    scheduler: str = "karras",
    filename_prefix: str = "decal_overlay",
) -> dict:
    """Construye el dict (API format) del workflow de UN decal / overlay con alpha.

    Args:
        decal: descripcion de la textura a superponer (ej. "blood splatter",
            "wall crack", "rust stain", "mud splatter", "burn mark", "dirt grunge",
            "scratch marks", "moss patch"). Se inserta en un prompt scaffold de
            decal aislado. No puede estar vacio.
        on_black: si True (defecto) el decal se genera sobre fondo NEGRO puro,
            pensado para extraer el alpha por luminancia con
            comfyui_matting_luma_to_alpha (translucido con falloff suave: sangre,
            humo, salpicadura). Si False se genera sobre fondo BLANCO (decals
            oscuros sobre superficies claras, o recorte/inversion por el caller).
            keyword-only.
        style: descriptor de estilo del decal (ej. "grunge decal, high detail",
            "stylized blood, painterly", "photorealistic rust", "cartoon crack").
            Pasa el MISMO style + checkpoint + (lora) a todos los decals de un set
            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.
        seed: semilla del KSampler. Misma seed + mismos decal/style -> misma
            textura; variar seed da variantes del mismo tipo de decal. keyword-only.
        lora: LoRA de estilo opcional en models/loras (ej. grunge/sangre/oxido).
            None = sin LoRA. keyword-only.
        lora_strength: fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0].
            keyword-only.
        negative: prompt negativo. None usa el negativo por defecto pensado para un
            decal aislado (una textura plana sin objetos/escena/profundidad/marco
            que delaten una composicion). 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 decal ('{decal}, {style}, isolated, on pure black|
        white background, texture overlay, game asset, ...') + LoRA de estilo
        opcional. NO lleva Rembg: con on_black=True el PNG resultante se convierte a
        RGBA con comfyui_matting_luma_to_alpha (luma=alpha) en un paso posterior.

    Raises:
        ValueError: si decal esta vacio.
    """
    from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow

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

    decal = decal.strip()
    lora_strength = max(0.0, min(2.0, float(lora_strength)))
    if negative is None:
        bg_neg = _DECAL_BG_NEG_ON_BLACK if on_black else _DECAL_BG_NEG_ON_WHITE
        neg = f"{_DECAL_NEGATIVE_COMMON}, {bg_neg}"
    else:
        neg = negative

    # Prompt scaffold de decal aislado: una textura plana, centrada, sobre fondo
    # UNIFORME y PLANO (negro por defecto -> luma-to-alpha despues), lista como
    # overlay recortable para superponer con blend mode en el motor. El fondo se
    # refuerza con "solid/flat" + repeticion para que no salga texturizado (el
    # style del decal, p.ej. "grunge", no debe contaminar el fondo).
    bg = (
        "on a solid pure black background, flat black backdrop"
        if on_black
        else "on a solid pure white background, flat white backdrop"
    )
    positive = (
        f"{decal}, {style}, single isolated decal, centered, {bg}, "
        "flat uniform background, plain backdrop, sticker, no scenery, "
        "texture overlay, game asset, high detail, sharp edges"
    )

    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
        )

    return wf


if __name__ == "__main__":
    import json

    wf = comfyui_build_decal_overlay_workflow(
        "blood splatter",
        on_black=True,
        style="grunge decal, high detail",
        seed=7,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
