"""Construye el workflow ComfyUI de UNA insignia / medalla / logro (API format).

Insignia de logro de juego (achievement, recompensa, rango): un trofeo, una
medalla con cinta, un escudo de logro, una estrella o un badge de rango que la UI
de achievements/recompensas pinta cuando el jugador desbloquea un hito. Tiene
TIER (bronce / plata / oro u otro grado) que distingue el nivel del logro,
centrado, fondo limpio uniforme, recortable a alpha, estilo consistente entre
insignias del mismo set. Es el builder hermano de comfyui_build_item_icon /
comfyui_build_skill_tree_node: mismo patron (PURO, dict API format) que compone
funciones existentes del registry, no reescribe el grafo.

DISTINTO de item_icon, status_effect_icon y skill_tree_node:
  - item_icon          -> objeto de inventario (espada, pocion): ilustracion de un
                          objeto suelto que el jugador usa/equipa, SIN tier, SIN cinta.
  - status_effect_icon -> simbolo de estado compacto que se superpone al retrato/barra
                          del personaje (envenenado, aturdido); SIN marco, 16-32 px.
  - skill_tree_node    -> nodo ENMARCADO de la pantalla de talentos con estado
                          unlocked/locked; vive en una rejilla de progresion.
  - achievement_badge  -> ESTO: la INSIGNIA DE LOGRO/RECOMPENSA = trofeo/medalla con
                          cinta + tier (bronce/plata/oro). El tier metalico y la forma
                          de medalla/trofeo son la firma del asset; se muestra en el
                          panel de logros, no en inventario ni en arbol de talentos.

Por que importa el tier: un sistema de achievements muestra el MISMO logro en varios
grados (bronce / plata / oro) segun el progreso o la dificultad alcanzada. Generar la
familia (tier="bronze" / "silver" / "gold") con el mismo badge/style/seed da las
caras coherentes del mismo logro; el motor intercambia el sprite segun el tier que el
jugador haya conseguido.

Cableado:

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

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

Por que Rembg y NO comfyui_matting_luma_to_alpha: una insignia es una pieza SOLIDA
con silueta definida (la medalla/trofeo perfila el borde, la cinta cuelga); rembg
recorta limpio la silueta dejando alpha. La luma-to-alpha es para translucidos sobre
negro (humo/fuego/runas brillantes) y aplanaria la medalla. Si el caller prefiere
recortar fuera del workflow (transparent=False) deja la imagen opaca sobre fondo
plano, recortable luego por el pipeline o el caller.

El mismo style + checkpoint + (lora) en todas las insignias del set hace que el panel
de logros combine visualmente: es la clave de un set coherente, igual que en los
iconos de inventario y los nodos del arbol de talentos.

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 insignias de logro: una sola medalla/trofeo
# centrado y recortable, sin escena ni personaje ni texto/marcas que ensucien la
# silueta. NO rechaza "ribbon/medal/trophy" (son parte del asset), pero si empuja
# contra escenas recargadas, multiples badges y fondos sucios.
_BADGE_NEGATIVE = (
    "blurry, lowres, busy background, cluttered, multiple badges, multiple medals, "
    "detailed scene, landscape, full body character, person, face, text, letters, "
    "words, numbers, watermark, signature, photo, photorealistic, realistic, "
    "jpeg artifacts, cropped, out of frame, deformed"
)

# Pistas visuales por tier: refuerzan en el prompt el aspecto metalico de la medalla
# segun su grado. El nombre del tier tambien se inserta literal en el prompt para que
# sea explicito. Un tier desconocido cae a la pista de "gold".
_TIER_HINTS = {
    "bronze": "bronze metal, copper tones, dark brown sheen, lowest tier",
    "silver": "silver metal, polished chrome, cool grey sheen, mid tier",
    "gold": "gold metal, shiny golden, warm yellow sheen, highest tier",
    "platinum": "platinum metal, bright white-silver, prestige sheen, top tier",
    "diamond": "diamond crystal, brilliant facets, prismatic shine, elite tier",
}


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_item_icon_workflow / comfyui_build_skill_tree_node_workflow:
    el nodo recorta la silueta de la insignia (medalla + cinta) 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_achievement_badge_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_achievement_badge_workflow(
    badge: str,
    *,
    tier: str = "gold",
    style: str = "game achievement badge, ornate",
    checkpoint: str = "IMG_dreamshaper_8.safetensors",
    size: int = 256,
    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 = "achievement_badge",
) -> dict:
    """Construye el dict (API format) del workflow de una insignia / medalla / logro.

    Args:
        badge: nombre / tema del logro que representa la insignia (ej. "dragon slayer",
            "first blood", "100 wins", "explorer", "marathon runner", "boss killer").
            Se inserta en un prompt scaffold de insignia. No puede estar vacio.
        tier: grado del logro: "bronze", "silver", "gold" (por defecto), "platinum" o
            "diamond". Define el aspecto metalico de la medalla. Genera la familia con
            el mismo badge/style/seed variando solo el tier para tener las caras
            coherentes del mismo logro. Cualquier otro valor se inserta literal (el
            tier hint cae al de "gold"). keyword-only.
        style: descriptor de estilo que mantiene consistentes las insignias de un set
            (ej. "game achievement badge, ornate", "flat minimal medal", "pixel art
            trophy"). Pasa el MISMO style + checkpoint + (lora) a todas las insignias
            del set 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). keyword-only.
        size: lado del cuadrado en px (width = height = size). 256 por defecto: las
            insignias de logro se muestran a tamano reducido en el panel. keyword-only.
        transparent: si True inyecta Rembg y el PNG sale con alpha (fondo recortado,
            la silueta de la medalla + cinta). Si False deja la insignia opaca sobre
            fondo plano, recortable luego por el caller/pipeline. keyword-only.
        seed: semilla del KSampler. Fija el mismo seed en la familia de tiers del mismo
            logro para que coincidan en composicion. keyword-only.
        lora: LoRA de estilo opcional en models/loras (ej.
            'SD15_detail_tweaker.safetensors'). 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
            insignias (una medalla limpia, fondo limpio, sin escena/personaje/texto).
            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 insignia ('{badge} achievement badge, {tier} tier
        ({tier hint}), {style}, medal with ribbon, centered, plain background, game UI
        reward, ...') + LoRA de estilo opcional + Rembg (si transparent). Es UNA
        insignia; un set de logros -> llamar por badge (y por tier) con el mismo
        style/checkpoint/lora y montar el panel en el motor.

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

    lora_strength = max(0.0, min(2.0, float(lora_strength)))
    neg = _BADGE_NEGATIVE if negative is None else negative

    badge_s = badge.strip()
    tier_s = (tier or "gold").strip()
    style_s = (style or "game achievement badge, ornate").strip()
    # Pista de aspecto metalico segun tier; tier desconocido -> aspecto de gold.
    tier_hint = _TIER_HINTS.get(tier_s.lower(), _TIER_HINTS["gold"])

    # Prompt scaffold de insignia de logro: medalla/trofeo con cinta, tier metalico,
    # centrado, fondo plano, recortable. badge/tier/style quedan reflejados literalmente.
    positive = (
        f"{badge_s} achievement badge, {tier_s} tier ({tier_hint}), {style_s}, "
        "medal with ribbon, centered, plain background, game UI reward, "
        "trophy emblem, clean, 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_achievement_badge_workflow(
        "dragon slayer",
        tier="gold",
        style="game achievement badge, ornate",
        transparent=True,
        seed=42,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
