"""Construye el workflow ComfyUI de UN edificio/estructura de escenario de juego (API format).

Estructura/edificio de mundo (casa, torre, castillo, tienda, posada, ruina, muralla,
puente, templo, almacen, faro...): UN building COMPLETO y centrado, con la perspectiva
del juego (isometrica o lateral), fondo limpio y uniforme recortable a alpha, estilo
consistente para poblar mapas/escenarios. Es el builder hermano de
comfyui_build_prop_object_workflow / comfyui_build_isometric_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
PEQUENO y suelto (barril, cofre, antorcha, planta, roca) que se deja encima de un tile.
Una *structure* es una EDIFICACION grande y completa que ocupa varios tiles y define el
escenario (la casa donde entra el jugador, la torre del mapa, el castillo del boss). Por
eso el scaffold empuja "full building, complete structure" y el negativo rechaza
"small object, single item, prop" para forzar el edificio entero, no un objeto suelto.

Cableado:

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

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

Por que Rembg y NO comfyui_matting_luma_to_alpha: un edificio es una masa SOLIDA con
silueta definida (muros, tejado, torres); rembg recorta limpio la silueta dejando alpha,
listo para soltar sobre el mapa. La luma-to-alpha es para translucidos sobre negro
(humo/fuego/magia), donde aplanaria la estructura. Para una edificacion solida rembg es
lo correcto. Si la estructura es etérea o tiene partes translucidas que quieras conservar,
pon transparent=False y recorta fuera del workflow.

Por que un solo edificio centrado y fondo plano: una structure se inserta como sprite/
objeto grande en el motor; el scaffold empuja a "single building, centered, plain
background, game asset" y el negativo por defecto rechaza "multiple buildings, small
object, prop, person, character, cropped, out of frame" para mantener UN edificio 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 edificios/estructuras: UN building completo y entero,
# fondo limpio, sin objetos pequenos/props sueltos (eso es prop_object), sin personas, sin
# texto/marcas ni recortes. No filtra ningun tipo de edificio (casa, torre, castillo,
# muralla, puente... son validos).
_STRUCTURE_NEGATIVE = (
    "small object, single item, prop, furniture, icon, "
    "multiple buildings, cityscape, person, people, character, creature, "
    "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 / item_icon: el nodo recorta
    la silueta del edificio 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_structure_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_structure_workflow(
    structure: str,
    *,
    view: str = "isometric",
    style: str = "game building",
    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 = "structure",
) -> dict:
    """Construye el dict (API format) del workflow de un edificio/estructura de escenario.

    Args:
        structure: descripcion de la edificacion (ej. "medieval house", "wizard tower",
            "stone castle", "merchant shop", "tavern inn", "ruined temple", "city wall",
            "wooden bridge", "lighthouse"). Se inserta en un prompt scaffold de building.
            No puede estar vacio.
        view: perspectiva del juego con la que se muestra el edificio. Se inserta como
            "{view} view" en el prompt (ej. "isometric", "side", "front", "3/4",
            "top-down"). Por defecto "isometric" (la mas comun para poblar mapas iso).
            keyword-only.
        style: descriptor de estilo que mantiene consistentes las estructuras del set
            (ej. "game building", "low poly stylized building", "pixel art building",
            "fantasy RPG building", "cartoon village building"). Pasa el MISMO view +
            style + checkpoint + (lora) a todos los edificios del mapa 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 mapa). Si False deja el edificio opaco sobre fondo
            plano, recortable luego por el caller/pipeline. keyword-only.
        seed: semilla del KSampler. Misma seed + misma structure/view/style -> mismo
            edificio. keyword-only.
        lora: LoRA de estilo/isometrica opcional en models/loras (ej.
            'SD15_isometric_game_assets.safetensors', 'SDXL_stylized_buildings.safetensors').
            Para escenarios isometricos coherentes, la LoRA iso ayuda a fijar el angulo
            2:1. 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
            estructuras (un edificio completo y entero, sin objetos pequenos/props, sin
            personas, 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 building ('{structure}, {view} view, {style}, full building,
        complete structure, centered, plain background, game asset, ...') + LoRA de estilo
        opcional + Image Rembg (si transparent). Es UN edificio; para poblar un mapa ->
        llamar por cada structure con el mismo view/style/checkpoint/(lora). Montar el set
        con comfyui_build_grid si se quiere un contact-sheet de las edificaciones.

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

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

    # Prompt scaffold de structure: un edificio COMPLETO (no un objeto suelto), centrado,
    # fondo plano, listo como asset de juego grande recortable. El "{view} view" fija la
    # perspectiva del escenario.
    view_clause = f"{view} view, " if view else ""
    positive = (
        f"{structure}, {view_clause}{style}, full building, complete structure, "
        "single building, centered, plain background, game asset, "
        "architecture, exterior, 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_structure_workflow(
        "medieval blacksmith shop",
        view="isometric",
        style="game building",
        transparent=True,
        seed=7,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
