"""Tests offline de comfyui_build_structure_workflow (estructura del dict, sin GPU)."""

import os
import sys

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from ml.comfyui_build_structure_workflow import (  # noqa: E402
    comfyui_build_structure_workflow,
)


def _classes(wf):
    return sorted({n["class_type"] for n in wf.values()})


def _by_class(wf, cls):
    return [n for n in wf.values() if n["class_type"] == cls]


def _id_of(wf, cls):
    return next(nid for nid, n in wf.items() if n["class_type"] == cls)


def _pos_with(wf, needle):
    return next(
        n for n in wf.values()
        if n["class_type"] == "CLIPTextEncode" and needle in n["inputs"]["text"]
    )


def test_golden_transparent_recipe():
    wf = comfyui_build_structure_workflow(
        "medieval blacksmith shop", view="isometric", transparent=True, seed=7
    )
    cls = _classes(wf)
    # Cadena base txt2img + Rembg para alpha.
    assert "CheckpointLoaderSimple" in cls
    assert "KSampler" in cls
    assert "VAEDecode" in cls
    assert "SaveImage" in cls
    assert "Image Rembg (Remove Background)" in cls
    # La structure aparece en el prompt positivo con el scaffold de edificio completo.
    pos = _pos_with(wf, "medieval blacksmith shop")
    txt = pos["inputs"]["text"]
    assert "full building" in txt
    assert "complete structure" in txt
    assert "centered" in txt
    assert "game asset" in txt
    assert "plain background" in txt
    # view reflejado como "{view} view".
    assert "isometric view" in txt
    # SaveImage toma la imagen del Rembg (no del VAEDecode).
    rembg_id = _id_of(wf, "Image Rembg (Remove Background)")
    save = next(n for n in wf.values() if n["class_type"] == "SaveImage")
    assert save["inputs"]["images"] == [rembg_id, 0]
    assert _by_class(wf, "Image Rembg (Remove Background)")[0]["inputs"]["transparency"] is True


def test_edge_opaque_no_rembg():
    wf = comfyui_build_structure_workflow("stone castle", transparent=False)
    assert "Image Rembg (Remove Background)" not in _classes(wf)
    # SaveImage toma del VAEDecode directamente.
    vd_id = _id_of(wf, "VAEDecode")
    save = next(n for n in wf.values() if n["class_type"] == "SaveImage")
    assert save["inputs"]["images"] == [vd_id, 0]


def test_edge_size_reflected():
    wf = comfyui_build_structure_workflow("wizard tower", size=768)
    latent = _by_class(wf, "EmptyLatentImage")[0]["inputs"]
    assert latent["width"] == 768
    assert latent["height"] == 768  # cuadrado


def test_edge_structure_at_start():
    # El scaffold arranca directamente con la structure.
    wf = comfyui_build_structure_workflow("tavern inn", transparent=False)
    pos = _pos_with(wf, "tavern inn")
    assert pos["inputs"]["text"].startswith("tavern inn")


def test_edge_view_reflected():
    # view distinto del default reflejado como "{view} view".
    wf = comfyui_build_structure_workflow(
        "wooden bridge", view="side", transparent=False
    )
    txt = _pos_with(wf, "wooden bridge")["inputs"]["text"]
    assert "side view" in txt


def test_edge_view_empty_no_clause():
    # view vacio -> no se inserta clausula "view" colgando.
    wf = comfyui_build_structure_workflow("city wall", view="", transparent=False)
    txt = _pos_with(wf, "city wall")["inputs"]["text"]
    assert " view," not in txt
    assert "full building" in txt


def test_edge_style_in_prompt():
    # Estilo del set reflejado.
    wf = comfyui_build_structure_workflow(
        "ruined temple", style="pixel art building", transparent=False
    )
    pos = _pos_with(wf, "ruined temple")
    assert "pixel art building" in pos["inputs"]["text"]


def test_edge_building_not_small_prop():
    # Por defecto el scaffold lo trata como edificio COMPLETO, no objeto pequeno suelto.
    wf = comfyui_build_structure_workflow("lighthouse", transparent=False)
    txt = _pos_with(wf, "lighthouse")["inputs"]["text"]
    assert "full building" in txt
    assert "single building" in txt
    # El negativo por defecto rechaza objeto pequeno/prop (diferenciado de prop_object).
    neg = next(
        n for n in wf.values()
        if n["class_type"] == "CLIPTextEncode" and "small object" in n["inputs"]["text"]
    )
    assert "prop" in neg["inputs"]["text"]
    assert "single item" in neg["inputs"]["text"]


def test_edge_lora_reflected():
    wf = comfyui_build_structure_workflow(
        "wizard tower",
        lora="SD15_isometric_game_assets.safetensors",
        lora_strength=0.9,
    )
    loras = _by_class(wf, "LoraLoader")
    assert len(loras) == 1
    assert loras[0]["inputs"]["lora_name"] == "SD15_isometric_game_assets.safetensors"
    assert loras[0]["inputs"]["strength_model"] == 0.9


def test_edge_transparent_default_true():
    # transparent por defecto True -> Rembg presente sin pasar el flag.
    wf = comfyui_build_structure_workflow("stone castle")
    assert "Image Rembg (Remove Background)" in _classes(wf)


def test_error_empty_structure():
    try:
        comfyui_build_structure_workflow("  ")
        assert False
    except ValueError as e:
        assert "structure" in str(e)


def test_determinism():
    a = comfyui_build_structure_workflow(
        "wizard tower", lora="SDXL_stylized_buildings.safetensors", seed=7
    )
    b = comfyui_build_structure_workflow(
        "wizard tower", lora="SDXL_stylized_buildings.safetensors", seed=7
    )
    assert a == b
