"""Tests offline (sin red, sin GPU) de comfyui_build_achievement_badge_workflow.

Verifican que el dict en API format se construye correctamente: clases presentes,
cableado del Rembg, prompt scaffold de insignia de logro, y reflejo de los argumentos
(badge, tier, style, size, transparent, lora). No tocan el servidor ComfyUI.
"""
import os
import sys

import pytest

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

from ml.comfyui_build_achievement_badge_workflow import (  # noqa: E402
    comfyui_build_achievement_badge_workflow,
)


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


def _positive_prompt(wf):
    """Texto positivo: el CLIPTextEncode al que apunta KSampler.positive."""
    ks = next(n for n in wf.values() if n["class_type"] == "KSampler")
    pos_id = ks["inputs"]["positive"][0]
    return wf[pos_id]["inputs"]["text"]


def test_golden_transparent():
    """Caso feliz: insignia transparente -> Rembg cableado, prompt de logro, clases base."""
    wf = comfyui_build_achievement_badge_workflow(
        "dragon slayer", tier="gold", transparent=True, seed=42
    )
    cls = _classes(wf)
    for expected in {
        "CheckpointLoaderSimple",
        "KSampler",
        "VAEDecode",
        "SaveImage",
        "Image Rembg (Remove Background)",
    }:
        assert expected in cls, f"falta clase {expected}"

    prompt = _positive_prompt(wf)
    assert "dragon slayer" in prompt
    assert "achievement badge" in prompt
    assert "gold tier" in prompt
    assert "medal with ribbon" in prompt
    assert "centered" in prompt
    assert "game UI reward" in prompt

    # SaveImage debe tomar la imagen 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 cuelga del VAEDecode."""
    wf = comfyui_build_achievement_badge_workflow("first blood", 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_reflected_square():
    """size se refleja como width == height (cuadrado). Default 256 (insignia compacta)."""
    wf = comfyui_build_achievement_badge_workflow("explorer", size=128)
    latent = next(n for n in wf.values() if n["class_type"] == "EmptyLatentImage")
    assert latent["inputs"]["width"] == 128
    assert latent["inputs"]["height"] == 128

    wf_default = comfyui_build_achievement_badge_workflow("explorer")
    latent_d = next(n for n in wf_default.values() if n["class_type"] == "EmptyLatentImage")
    assert latent_d["inputs"]["width"] == 256
    assert latent_d["inputs"]["height"] == 256


def test_edge_tier_gold_default_hint():
    """tier='gold' (default) se refleja literal y arrastra la pista metalica de oro."""
    wf = comfyui_build_achievement_badge_workflow("boss killer")
    prompt = _positive_prompt(wf)
    assert "gold tier" in prompt
    assert "gold metal" in prompt


def test_edge_tier_bronze_reflected():
    """tier='bronze' se refleja literal y arrastra la pista metalica de bronce."""
    wf = comfyui_build_achievement_badge_workflow("rookie", tier="bronze")
    prompt = _positive_prompt(wf)
    assert "bronze tier" in prompt
    assert "bronze metal" in prompt


def test_edge_tier_silver_reflected():
    """tier='silver' se refleja literal y arrastra la pista metalica de plata."""
    wf = comfyui_build_achievement_badge_workflow("veteran", tier="silver")
    prompt = _positive_prompt(wf)
    assert "silver tier" in prompt
    assert "silver metal" in prompt


def test_edge_tier_unknown_falls_back_to_gold_hint():
    """tier desconocido se inserta literal pero la pista cae a la de gold."""
    wf = comfyui_build_achievement_badge_workflow("mythic", tier="legendary")
    prompt = _positive_prompt(wf)
    assert "legendary tier" in prompt
    assert "gold metal" in prompt  # hint fallback


def test_edge_style_reflected():
    """style se inserta en el prompt positivo."""
    wf = comfyui_build_achievement_badge_workflow(
        "speedrun", style="pixel art trophy"
    )
    assert "pixel art trophy" in _positive_prompt(wf)


def test_edge_badge_reflected():
    """badge se inserta literal en el prompt positivo."""
    wf = comfyui_build_achievement_badge_workflow("marathon runner")
    assert "marathon runner" in _positive_prompt(wf)


def test_edge_lora_injected():
    """lora -> LoraLoader presente con la fuerza dada."""
    wf = comfyui_build_achievement_badge_workflow(
        "collector", lora="SD15_detail_tweaker.safetensors", lora_strength=0.8
    )
    loras = [n for n in wf.values() if n["class_type"] == "LoraLoader"]
    assert len(loras) == 1
    assert loras[0]["inputs"]["lora_name"] == "SD15_detail_tweaker.safetensors"
    assert loras[0]["inputs"]["strength_model"] == pytest.approx(0.8)


def test_error_empty_badge():
    """badge vacio -> ValueError."""
    with pytest.raises(ValueError):
        comfyui_build_achievement_badge_workflow("   ")


def test_determinism():
    """Mismos argumentos -> mismo dict (funcion pura)."""
    a = comfyui_build_achievement_badge_workflow("undefeated", seed=7)
    b = comfyui_build_achievement_badge_workflow("undefeated", seed=7)
    assert a == b
