"""Construye el workflow ComfyUI de UNA textura de particula con alpha (API format).

Una textura de particula es UN elemento pequeño y reutilizable que alimenta el
sistema de particulas del motor (Godot GPUParticles2D / GPUParticles3D, Unity VFX
Graph / Shuriken): chispa, humo, polvo, destello/flare, gota, copo, hoja, circulo
de energia. El motor instancia MILES de copias de esta unica textura y las anima
(spawn, velocidad, fade, color over lifetime). Por eso se quiere UN solo elemento
limpio, centrado y aislado, NO una escena ni una secuencia animada.

La tecnica gamedev correcta para una particula translucida es generarla sobre
fondo NEGRO puro y convertir la luminancia en alpha con
`comfyui_matting_luma_to_alpha`: brillante -> opaco, negro -> transparente. Eso
preserva el degradado de los bordes (glow, falloff) que el additive blend del
motor necesita para que el solape de particulas brille de forma natural (un
matting binario tipo rembg destruiria ese falloff). Por eso esta textura SIEMPRE
se genera sobre negro y el scaffold empuja "isolated on pure black background": el
PNG resultante esta pensado para pasar por luma-to-alpha en un paso aparte.

`soft` controla el caracter del borde: True (defecto) -> "soft glow, soft falloff,
feathered edges" (humo, polvo, destello, circulo de energia, gota difusa); False ->
"crisp sharp edges, high contrast" (chispa nitida, copo de nieve cristalino, hoja
con silueta definida). En ambos casos el fondo sigue siendo negro plano.

Es el builder hermano de comfyui_build_decal_overlay_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 una
particula translucida es luma-to-alpha (post-proceso de disco), no un nodo.

Diferencia con comfyui_build_vfx_spritesheet_workflow: aquel genera una SECUENCIA
animada de N frames de un efecto (humo en bucle, fuego que arde) via AnimateDiff;
este genera UNA textura estatica reutilizable que el motor anima a miles. Un
spritesheet es la animacion completa; una particle texture es el "ladrillo" que el
sistema de particulas replica.

Cableado:

    CheckpointLoaderSimple -> [LoraLoader opcional de estilo] -> KSampler
                          -> CLIPTextEncode (prompt scaffold particula aislada) ...
                          -> VAEDecode -> SaveImage  (particula sobre negro)

Compone:
  - comfyui_build_txt2img_workflow  -> base txt2img cuadrada
  - comfyui_inject_lora             -> LoRA de estilo opcional

Pipeline despues de generar (no en este builder):
  comfyui_submit_workflow -> comfyui_wait_result -> comfyui_fetch_output_image
  -> comfyui_matting_luma_to_alpha  -> PNG RGBA listo para el sistema de particulas

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 textura de particula: UN solo elemento limpio sobre
# fondo negro plano, SIN escena, objeto 3D solido, multiples elementos, profundidad
# ni marco/borde que delaten una composicion o arruinen el alpha por luminancia.
# El fondo DEBE quedar negro plano para que el luma-to-alpha no recoja basura.
_PARTICLE_NEGATIVE_COMMON = (
    "3d render, solid object, scene, landscape, character, person, creature, "
    "multiple particles, particle system, tiled pattern, grid, collage, "
    "depth of field, perspective, vignette, frame, border, drop shadow, "
    "textured background, gray background, white background, busy background, "
    "background pattern, gradient background, "
    "text, watermark, signature, logo, blurry, low quality, jpeg artifacts"
)


def comfyui_build_particle_texture_workflow(
    particle: str,
    *,
    soft: bool = True,
    style: str = "particle texture, soft glow",
    checkpoint: str = "IMG_dreamshaper_8.safetensors",
    size: int = 256,
    seed: int = 0,
    lora: str | None = None,
    lora_strength: float = 1.0,
    negative: str | None = None,
    steps: int = 24,
    cfg: float = 7.0,
    sampler_name: str = "dpmpp_2m",
    scheduler: str = "karras",
    filename_prefix: str = "particle_texture",
) -> dict:
    """Construye el dict (API format) del workflow de UNA textura de particula.

    Args:
        particle: descripcion del elemento de particula (ej. "spark", "smoke
            puff", "dust mote", "glowing flare", "water droplet", "snowflake",
            "falling leaf", "energy ring"). Se inserta en un prompt scaffold de
            particula aislada sobre negro. No puede estar vacio.
        soft: si True (defecto) la particula se genera con bordes suaves y glow
            difuso ("soft glow, soft falloff, feathered edges": humo, polvo,
            destello, circulo de energia, gota); si False con bordes nitidos y
            alto contraste ("crisp sharp edges, high contrast": chispa, copo de
            nieve, hoja con silueta). En ambos casos el fondo es negro plano.
            keyword-only.
        style: descriptor de estilo de la particula (ej. "particle texture, soft
            glow", "stylized vfx, painterly", "photoreal smoke", "anime sparkle").
            Pasa el MISMO style + checkpoint + (lora) a todas las particulas 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 512/768). keyword-only.
        size: lado del cuadrado en px (width = height = size). 256 por defecto:
            una textura de particula es pequeña y se replica a miles, no necesita
            resolucion alta. keyword-only.
        seed: semilla del KSampler. Misma seed + mismos particle/style -> misma
            textura; variar seed da variantes del mismo tipo de particula.
            keyword-only.
        lora: LoRA de estilo opcional en models/loras. 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
            una particula aislada (un solo elemento sobre negro plano, sin escena
            ni multiples elementos ni fondo texturizado). 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 particula ('{particle} particle, {style},
        isolated on pure black background, <soft|sharp> edges, single element, for
        game particle system, ...') + LoRA de estilo opcional. NO lleva Rembg: el
        PNG resultante se convierte a RGBA con comfyui_matting_luma_to_alpha
        (luma=alpha) en un paso posterior. UNA particula; variar seed da variantes.

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

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

    particle = particle.strip()
    lora_strength = max(0.0, min(2.0, float(lora_strength)))
    neg = _PARTICLE_NEGATIVE_COMMON if negative is None else negative

    # Caracter del borde segun soft: suave/glow difuso para translucidos (humo,
    # destello, circulo de energia) o nitido/cristalino para particulas con
    # silueta (chispa, copo). En ambos casos el fondo permanece negro plano.
    edge = (
        "soft edges, soft glow, gentle falloff, feathered"
        if soft
        else "crisp sharp edges, high contrast, well defined silhouette"
    )

    # Prompt scaffold de particula aislada: UN solo elemento, centrado, sobre fondo
    # NEGRO puro y plano (insumo de luma-to-alpha despues), listo como ladrillo del
    # sistema de particulas del motor. El fondo se refuerza con "solid/flat" para
    # que no salga texturizado y el alpha por luminancia no recoja basura.
    positive = (
        f"{particle} particle, {style}, isolated on pure black background, "
        f"{edge}, single element, centered, for game particle system, "
        "flat solid black backdrop, vfx sprite, glowing, 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
        )

    return wf


if __name__ == "__main__":
    import json

    wf = comfyui_build_particle_texture_workflow(
        "spark",
        soft=False,
        style="particle texture, glowing ember",
        seed=7,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
