"""Tests offline de comfyui_build_foliage_set_workflow (sin red ni GPU).

Valida que el dict del workflow refleja plant/view/style/transparent y la
estructura de nodos, igual que sus hermanos prop_object/structure.
"""
import os
import sys

import pytest

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

from ml.comfyui_build_foliage_set_workflow import comfyui_build_foliage_set_workflow


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


def _positive_prompt(wf):
    """Devuelve el texto positivo (el primer CLIPTextEncode cableado al KSampler.positive)."""
    ksampler = next(n for n in wf.values() if n["class_type"] == "KSampler")
    pos_id = ksampler["inputs"]["positive"][0]
    return wf[pos_id]["inputs"]["text"]


def test_golden_transparent():
    """Caso feliz: arbol con alpha. Clases completas + scaffold + Rembg cableado."""
    wf = comfyui_build_foliage_set_workflow("a large oak tree", seed=7)
    cls = _classes(wf)
    assert "CheckpointLoaderSimple" in cls
    assert "KSampler" in cls
    assert "VAEDecode" in cls
    assert "SaveImage" in cls
    assert "Image Rembg (Remove Background)" in cls

    prompt = _positive_prompt(wf)
    assert "a large oak tree" in prompt
    assert "single plant element" in prompt
    assert "game nature asset" in prompt
    assert "natural vegetation" in prompt
    assert "centered" in prompt
    assert "plain background" in prompt
    assert "side view" in prompt  # view por defecto

    # SaveImage debe consumir la salida del Rembg, no del VAEDecode.
    save = next(n for n in wf.values() if n["class_type"] == "SaveImage")
    rembg_id = next(nid for nid, n in wf.items() if n["class_type"] == "Image Rembg (Remove Background)")
    assert save["inputs"]["images"][0] == rembg_id
    rembg = wf[rembg_id]
    assert rembg["inputs"]["transparency"] is True


def test_edge_transparent_false_no_rembg():
    """transparent=False: sin nodo Rembg, SaveImage <- VAEDecode."""
    wf = comfyui_build_foliage_set_workflow("a fern", transparent=False)
    assert "Image Rembg (Remove Background)" not in _classes(wf)
    save = next(n for n in wf.values() if n["class_type"] == "SaveImage")
    vae_id = next(nid for nid, n in wf.items() if n["class_type"] == "VAEDecode")
    assert save["inputs"]["images"][0] == vae_id


def test_edge_size_square():
    """size se aplica como cuadrado width==height."""
    wf = comfyui_build_foliage_set_workflow("tall grass", size=768)
    latent = next(n for n in wf.values() if n["class_type"] == "EmptyLatentImage")
    assert latent["inputs"]["width"] == 768
    assert latent["inputs"]["height"] == 768


def test_edge_plant_at_start():
    """El plant abre el scaffold (no queda enterrado)."""
    wf = comfyui_build_foliage_set_workflow("a glowing mushroom")
    assert _positive_prompt(wf).startswith("a glowing mushroom,")


def test_edge_view_reflected():
    """view se refleja como '{view} view'."""
    wf = comfyui_build_foliage_set_workflow("a desert cactus", view="top-down")
    assert "top-down view" in _positive_prompt(wf)


def test_edge_view_empty_no_clause():
    """view vacio -> sin clausula 'view' colgando."""
    wf = comfyui_build_foliage_set_workflow("reeds", view="")
    prompt = _positive_prompt(wf)
    assert "view," not in prompt
    assert "reeds," in prompt


def test_edge_style_reflected():
    """style del set aparece en el prompt."""
    wf = comfyui_build_foliage_set_workflow(
        "a small bush", style="pixel art foliage"
    )
    assert "pixel art foliage" in _positive_prompt(wf)


def test_edge_negative_rejects_manmade_and_buildings():
    """Negativo por defecto diferencia foliage de prop/structure (rechaza manmade/building)."""
    wf = comfyui_build_foliage_set_workflow("a fern")
    ksampler = next(n for n in wf.values() if n["class_type"] == "KSampler")
    neg_id = ksampler["inputs"]["negative"][0]
    neg = wf[neg_id]["inputs"]["text"]
    assert "building" in neg
    assert "manmade object" in neg or "barrel" in neg
    assert "person" in neg
    assert "dense forest" in neg  # un elemento, no un bosque


def test_edge_lora_present():
    """lora -> LoraLoader presente con la fuerza pasada."""
    wf = comfyui_build_foliage_set_workflow(
        "a cluster of red flowers", lora="SD15_stylized_nature.safetensors",
        lora_strength=0.8,
    )
    assert "LoraLoader" in _classes(wf)
    lora_node = next(n for n in wf.values() if n["class_type"] == "LoraLoader")
    assert lora_node["inputs"]["strength_model"] == pytest.approx(0.8)


def test_edge_lora_strength_clamped():
    """lora_strength fuera de [0,2] se clampa."""
    wf = comfyui_build_foliage_set_workflow(
        "a fallen log", lora="x.safetensors", lora_strength=9.0
    )
    lora_node = next(n for n in wf.values() if n["class_type"] == "LoraLoader")
    assert lora_node["inputs"]["strength_model"] == pytest.approx(2.0)


def test_edge_transparent_default_true():
    """transparent es True por defecto -> Rembg presente."""
    wf = comfyui_build_foliage_set_workflow("a patch of ivy")
    assert "Image Rembg (Remove Background)" in _classes(wf)


def test_error_empty_plant():
    """plant vacio -> ValueError."""
    with pytest.raises(ValueError):
        comfyui_build_foliage_set_workflow("   ")


def test_determinism():
    """Mismos args -> mismo dict."""
    a = comfyui_build_foliage_set_workflow("a fern", seed=3)
    b = comfyui_build_foliage_set_workflow("a fern", seed=3)
    assert a == b
