"""Construye el workflow ComfyUI de UN elemento de vegetacion/foliage de escenario (API format).

Vegetacion/foliage de mundo (arbol, arbusto, hierba alta, flores, helecho, hongo,
cactus, tronco caido, juncos, hiedra, matorral...): UN elemento de naturaleza
AISLADO, centrado y con la perspectiva del juego (lateral o cenital), fondo limpio
y uniforme recortable a alpha, estilo consistente para poblar escenarios. 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 escena
MANUFACTURADO e inanimado (barril, cofre, antorcha, mueble, fuente) que se deja sobre
un tile. Un *foliage* es un elemento de NATURALEZA ORGANICA (planta, arbol, flor,
hongo, hierba): se planta en el escenario para vestir el terreno, conserva su silueta
vegetal irregular y se reparte a mansalva por el nivel. Por eso el scaffold empuja
"single plant element, nature asset, natural vegetation, organic" en vez del
"single object, scene prop" de los props, y el negativo rechaza objetos
manufacturados/edificios/personas para mantener UN elemento vegetal limpio.

Cableado:

    CheckpointLoaderSimple -> [LoraLoader opcional de estilo] -> KSampler
                          -> CLIPTextEncode (prompt scaffold de foliage) ...
                          -> 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: una planta es una masa OPACA con
silueta definida (hojas, ramas, tallos); rembg recorta limpio la silueta dejando
alpha, listo para soltar sobre el terreno. La luma-to-alpha es para translucidos
sobre negro (humo/fuego/magia), donde aplanaria el follaje. Para vegetacion solida
(arbol, arbusto, flor, hongo) rembg es lo correcto. Si el elemento es etereo o
translucido (planta bioluminiscente, espora flotante) y se quiere conservar la
translucidez, recortar fuera del workflow (transparent=False) y componer con
luma-to-alpha en un paso aparte.

Por que un solo elemento centrado y fondo plano: una planta se inserta como sprite/
objeto suelto en el motor y se repite por el nivel; el scaffold empuja a "single plant
element, centered, plain background, game nature asset" y el negativo por defecto
rechaza "person, character, building, multiple plants, dense forest, cropped, out of
frame" para mantener UN elemento entero, organico y recortable (no un bosque ni un
paisaje).

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 vegetacion/foliage: UN elemento vegetal entero,
# organico, fondo limpio, sin objetos manufacturados (eso es prop_object), sin
# edificios (eso es structure), sin personas/criaturas, sin convertirlo en un bosque
# o paisaje denso, sin texto/marcas ni recortes. No filtra ningun tipo de planta
# (arbol, arbusto, flor, hongo, cactus, helecho... son validos).
_FOLIAGE_NEGATIVE = (
    "person, people, character, creature, animal, hands, face, "
    "building, house, structure, barrel, furniture, manmade object, "
    "multiple plants, dense forest, jungle, landscape, scenery background, "
    "pot, planter, vase, "
    "cluttered, 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 vegetal del elemento 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_foliage_set_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_foliage_set_workflow(
    plant: str,
    *,
    view: str = "side",
    style: str = "game foliage, stylized",
    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 = "foliage_set",
) -> dict:
    """Construye el dict (API format) del workflow de un elemento de vegetacion/foliage.

    Args:
        plant: descripcion del elemento de naturaleza (ej. "a large oak tree",
            "a small bush", "tall grass", "a cluster of red flowers", "a fern",
            "a glowing mushroom", "a desert cactus", "a fallen log", "reeds",
            "a patch of ivy"). Se inserta en un prompt scaffold de foliage. No puede
            estar vacio.
        view: perspectiva del juego con la que se muestra la planta. Se inserta como
            "{view} view" en el prompt (ej. "side", "top-down", "front", "3/4",
            "isometric"). Por defecto "side" (la mas comun para vegetacion de
            plataformas/RPG lateral). Vacio/None -> sin clausula de vista.
            keyword-only.
        style: descriptor de estilo que mantiene consistente la vegetacion del set
            (ej. "game foliage, stylized", "low poly stylized plant", "pixel art
            foliage", "painterly fantasy plant", "cartoon nature asset"). Pasa el
            MISMO view + style + checkpoint + (lora) a todas las plantas 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 terreno). Si False deja la planta
            opaca sobre fondo plano, recortable luego por el caller/pipeline.
            keyword-only.
        seed: semilla del KSampler. Misma seed + mismo plant/view/style -> misma
            planta. keyword-only.
        lora: LoRA de estilo opcional en models/loras (ej.
            'SD15_stylized_nature.safetensors', 'SDXL_painterly_plants.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
            foliage (un elemento vegetal entero, organico, sin objetos
            manufacturados/edificios/personas, sin bosque/paisaje denso, 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 foliage ('{plant}, {view} view, {style}, single plant
        element, centered, plain background, game nature asset, ...') + LoRA de estilo
        opcional + Image Rembg (si transparent). Es UN elemento; para poblar un nivel
        -> llamar por cada plant con el mismo view/style/checkpoint/(lora). Montar el
        set con comfyui_build_grid si se quiere un contact-sheet de la vegetacion.

    Raises:
        ValueError: si plant 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 plant or not plant.strip():
        raise ValueError(
            "comfyui_build_foliage_set_workflow: 'plant' no puede estar vacio"
        )

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

    # Prompt scaffold de foliage: un elemento vegetal AISLADO (no un bosque), centrado,
    # fondo plano, listo como asset de naturaleza suelto (sprite/objeto) recortable. El
    # "{view} view" fija la perspectiva del escenario.
    view_clause = f"{view} view, " if view else ""
    positive = (
        f"{plant}, {view_clause}{style}, single plant element, centered, "
        "plain background, game nature asset, natural vegetation, organic, "
        "isolated plant, 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_foliage_set_workflow(
        "a large oak tree",
        view="side",
        style="game foliage, stylized",
        transparent=True,
        seed=7,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
