"""Tests offline de comfyui_build_rune_glyph_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_rune_glyph_workflow import (  # noqa: E402
    comfyui_build_rune_glyph_workflow,
)

import pytest  # noqa: E402


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


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_rune_glow_on_black_recipe():
    wf = comfyui_build_rune_glyph_workflow(
        "circular summoning rune", glow=True, seed=11
    )
    cls = _classes(wf)
    # Cadena base txt2img pura: NO lleva Rembg (el matting es luma-to-alpha aparte).
    assert "CheckpointLoaderSimple" in cls
    assert "KSampler" in cls
    assert "VAEDecode" in cls
    assert "SaveImage" in cls
    assert "Image Rembg (Remove Background)" not in cls
    # El glifo + el aislamiento + el resplandor sobre negro aparecen en el positivo.
    pos = _pos_with(wf, "circular summoning rune")
    txt = pos["inputs"]["text"]
    assert "magic symbol" in txt
    assert "isolated" in txt
    assert "glowing on a solid pure black background" in txt
    assert "centered" in txt
    assert "game asset" in txt
    # SaveImage toma del VAEDecode directamente (sin recorte intermedio).
    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_glow_toggles_background():
    # glow=True -> resplandor sobre negro plano (pensado para luma->alpha).
    wf_glow = comfyui_build_rune_glyph_workflow("fire spell glyph", glow=True)
    pos_glow = _pos_with(wf_glow, "fire spell glyph")["inputs"]["text"]
    assert "glowing on a solid pure black background" in pos_glow
    assert "plain flat background" not in pos_glow
    # glow=False -> runa mate sobre fondo plano, sin resplandor; el negativo
    # rechaza el glow para una marca grabada.
    wf_plain = comfyui_build_rune_glyph_workflow("fire spell glyph", glow=False)
    pos_plain = _pos_with(wf_plain, "fire spell glyph")["inputs"]["text"]
    assert "plain flat background" in pos_plain
    assert "matte engraved symbol" in pos_plain
    assert "glowing on a solid pure black background" not in pos_plain
    neg_plain = next(
        n["inputs"]["text"]
        for n in wf_plain.values()
        if n["class_type"] == "CLIPTextEncode"
        and "fire spell glyph" not in n["inputs"]["text"]
    )
    assert "glow" in neg_plain  # la runa mate rechaza el resplandor


def test_edge_glyph_and_style_reflected():
    # glyph y style se reflejan literalmente en el prompt positivo.
    wf = comfyui_build_rune_glyph_workflow(
        "demonic sigil", style="fiery demonic sigil, ember red", seed=3
    )
    txt = _pos_with(wf, "demonic sigil")["inputs"]["text"]
    assert "demonic sigil" in txt
    assert "fiery demonic sigil, ember red" in txt


def test_edge_seed_and_size_propagate():
    wf = comfyui_build_rune_glyph_workflow("magic circle", size=384, seed=99)
    ks = next(n for n in wf.values() if n["class_type"] == "KSampler")
    assert ks["inputs"]["seed"] == 99
    latent = next(n for n in wf.values() if n["class_type"] == "EmptyLatentImage")
    assert latent["inputs"]["width"] == 384
    assert latent["inputs"]["height"] == 384


def test_edge_lora_injected_when_requested():
    # Sin LoRA -> no LoraLoader; con LoRA -> aparece el nodo.
    wf_no = comfyui_build_rune_glyph_workflow("ward symbol")
    assert "LoraLoader" not in _classes(wf_no)
    wf_lora = comfyui_build_rune_glyph_workflow(
        "ward symbol", lora="SD15_arcane_style.safetensors", lora_strength=0.8
    )
    assert "LoraLoader" in _classes(wf_lora)
    ll = next(n for n in wf_lora.values() if n["class_type"] == "LoraLoader")
    assert ll["inputs"]["strength_model"] == pytest.approx(0.8)
    assert ll["inputs"]["strength_clip"] == pytest.approx(0.8)


def test_edge_lora_strength_clamped():
    wf = comfyui_build_rune_glyph_workflow(
        "rune", lora="x.safetensors", lora_strength=5.0
    )
    ll = next(n for n in wf.values() if n["class_type"] == "LoraLoader")
    assert ll["inputs"]["strength_model"] == pytest.approx(2.0)  # clamp a 2.0


def test_error_empty_glyph_raises():
    with pytest.raises(ValueError):
        comfyui_build_rune_glyph_workflow("")
    with pytest.raises(ValueError):
        comfyui_build_rune_glyph_workflow("   ")


def test_purity_no_global_mutation():
    # Dos llamadas identicas producen dicts equivalentes (determinismo).
    a = comfyui_build_rune_glyph_workflow("ancient rune", seed=1)
    b = comfyui_build_rune_glyph_workflow("ancient rune", seed=1)
    assert _classes(a) == _classes(b)
    assert _pos_with(a, "ancient rune")["inputs"]["text"] == _pos_with(
        b, "ancient rune"
    )["inputs"]["text"]
