"""comfyui_get_gamedev_style_preset — recetas de ESTILO curadas y reutilizables para gamedev.

Un *style preset* es la receta de un look visual coherente que un dev quiere aplicar
a TODOS los assets de su juego de una vez ("todo en estilo Game Boy", "estilo Ghibli",
"pixel-art retro 16-bit"). En vez de repetir a mano el mismo `style`/`checkpoint`/`lora`/
`negative` y el mismo post-proceso en cada builder gamedev, el preset los empaqueta como
DATOS puros que cualquier builder o pipeline del grupo `gamedev-2d` consume vía su helper
hermano `comfyui_apply_style_preset`.

Diseno (issue 0087, crecer por composicion no por inflado): se eligio (A) una funcion pura
de presets + un helper de aplicacion, NO (B) un pipeline monolitico, porque:

  - Los presets son datos puros: reutilizables por CUALQUIER builder de sujeto
    (item_icon, enemy_creature, prop_object, ...) y por el pipeline ya existente
    `comfyui_generate_asset_pack_oneshot` (que comparte checkpoint/style/lora) sin acoplar
    sus firmas heterogeneas.
  - El helper `comfyui_apply_style_preset` traduce el preset a los kwargs comunes de los
    builders + indica el post-proceso. Asi el preset se aplica al builder ANTES de construir
    el grafo (eligiendo checkpoint/lora coherentes), no parcheando el dict despues.

Cada preset es un dict con estos campos:

    {
        "name": str,             # identificador del estilo
        "subject_prefix": str,   # texto que precede al subject del usuario
        "subject_suffix": str,   # texto que sigue al subject del usuario
        "style": str,            # descriptor de estilo -> kwarg `style` del builder
        "negative": str,         # negativo extra del estilo -> se mergea con el del builder
        "checkpoint": str,       # checkpoint recomendado (SD1.5 vs SDXL segun el LoRA)
        "lora": str | None,      # LoRA del estilo (None = solo prompt + post)
        "lora_strength": float,  # fuerza del LoRA
        "size": int,             # resolucion recomendada (SDXL pide mas)
        "transparent": bool,     # recorte a alpha recomendado para este look
        "post": dict,            # post-proceso CPU; {"pixelize": {...}} o {} si no aplica
        "notes": str,            # como se logro el look y caveats
    }

Funcion pura: sin red, sin I/O, sin estado. Devuelve copias profundas para que el caller
no mute el registro interno. Extensible: anadir un estilo = una entrada en `_PRESETS`.
"""
from __future__ import annotations

import copy


# Modelos verificados presentes en el servidor (8GB lowvram, modelos en /mnt/2tb):
#   checkpoints: IMG_dreamshaper_8 (SD1.5), IMG_juggernaut_xl_v11 (SDXL)
#   loras:       SDXL_pixel-art (SDXL), SD15_watercolor_style (SD1.5), ...
# Si se anade un estilo con un LoRA nuevo, descargar antes a models/loras y verificar
# que el `checkpoint` casa con su base (un LoRA SDXL exige checkpoint SDXL).
_PRESETS: dict[str, dict] = {
    # Game Boy DMG: paleta de 4 tonos verde + dithering 1-bit + lowres. Sin LoRA:
    # el prompt empuja a monocromo simple y el post `pixelize` con la paleta builtin
    # "game-boy" sella el look icónico de 4 tonos. transparent=False para que el verde
    # cubra todo el frame (recorte a alpha es ortogonal y se hace despues si se quiere).
    "gameboy": {
        "name": "gameboy",
        "subject_prefix": "",
        "subject_suffix": ", 8-bit, simple shapes, limited palette, retro handheld",
        "style": "Game Boy DMG game art, monochrome green, retro handheld sprite, low detail, flat shading",
        "negative": "color, colorful, vibrant, photorealistic, photo, 3d render, smooth gradient, high detail, realistic lighting",
        "checkpoint": "IMG_dreamshaper_8.safetensors",
        "lora": None,
        "lora_strength": 1.0,
        "size": 512,
        "transparent": False,
        "post": {
            "pixelize": {
                "downscale": 6,
                "colors": 4,
                "palette": "game-boy",
                "dither": True,
                "upscale_back": True,
            }
        },
        "notes": (
            "Sin LoRA: el look DMG lo da el post-proceso pixelize con la paleta builtin "
            "'game-boy' (4 tonos verde 0f380f/306230/8bac0f/9bbc0f) + dithering. El prompt "
            "solo empuja a formas simples y monocromo. transparent=False para que la paleta "
            "verde cubra todo el frame."
        ),
    },
    # Studio Ghibli: anime pintado a mano, colores suaves, fondos acuarela. No hay un LoRA
    # Ghibli dedicado instalado (no se descargo ninguno gated/de pago); el look pintado se
    # logra con SD15_watercolor_style (LoRA gratis ya instalado) a fuerza media + prompt
    # Ghibli. Sin post-proceso (es ilustracion pintada, no pixelart).
    "ghibli": {
        "name": "ghibli",
        "subject_prefix": "",
        "subject_suffix": ", anime illustration, soft lighting, painterly",
        "style": "Studio Ghibli style, hand-painted anime, soft colors, watercolor background, whimsical, gentle, detailed illustration",
        "negative": "photo, photorealistic, 3d render, harsh shadows, pixel art, lowres, deformed, text, watermark, signature",
        "checkpoint": "IMG_dreamshaper_8.safetensors",
        "lora": "SD15_watercolor_style.safetensors",
        "lora_strength": 0.7,
        "size": 512,
        "transparent": False,
        "post": {},
        "notes": (
            "No hay LoRA Ghibli dedicado instalado y no se descargo ninguno gated/de pago "
            "(error-path: degradar a LoRA gratis + prompt). SD15_watercolor_style (gratis, ya "
            "instalado) a strength 0.7 + prompt Ghibli da el look pintado/acuarela. Un LoRA "
            "Ghibli especifico de Civitai mejoraria el parecido facial — pendiente humano si "
            "se quiere. transparent=False para conservar el fondo acuarela."
        ),
    },
    # Pixel-art retro 16-bit (SNES/Genesis): reutiliza el LoRA SDXL_pixel-art ya instalado
    # (es SDXL -> exige checkpoint SDXL IMG_juggernaut_xl_v11 y resolucion mayor). El post
    # pixelize a 16 colores sella los pixeles duros (el LoRA da el estilo, el post el grid).
    "pixel-art-retro": {
        "name": "pixel-art-retro",
        "subject_prefix": "",
        "subject_suffix": ", pixel art, game sprite, crisp pixels",
        "style": "16-bit pixel art, SNES JRPG sprite, retro game, limited palette, clean outline, flat colors",
        "negative": "blurry, smooth, photorealistic, 3d render, realistic, antialiasing, soft, gradient, noise",
        "checkpoint": "IMG_juggernaut_xl_v11.safetensors",
        "lora": "SDXL_pixel-art.safetensors",
        "lora_strength": 1.0,
        "size": 768,
        "transparent": False,
        "post": {
            "pixelize": {
                "downscale": 8,
                "colors": 16,
                "palette": None,
                "dither": False,
                "upscale_back": True,
            }
        },
        "notes": (
            "Reutiliza SDXL_pixel-art.safetensors (SDXL, ya instalado) -> requiere checkpoint "
            "SDXL IMG_juggernaut_xl_v11 y size 768 (a 512 SDXL+SDXL_pixel-art pierde calidad). El "
            "post pixelize a 16 colores (paleta auto MEDIANCUT) da el grid duro 16-bit. OOM en "
            "8GB -> bajar size a 512 (NO matar procesos). El LoRA da el estilo; el post el grid."
        ),
    },
    # Cyberpunk neón: ciudad nocturna, luces de neón, glow, alto contraste, atmósfera
    # Blade Runner. No hay un LoRA cyberpunk gratis instalado en el servidor; el look se
    # logra con PROMPT puro sobre SD1.5 (IMG_dreamshaper_8 lo cubre bien) — es un estilo muy
    # dirigible por prompt. Sin post (es ilustración con gradientes/glow, NO pixelart;
    # pixelizar mataría el brillo). transparent=False para conservar el fondo neón.
    "cyberpunk-neon": {
        "name": "cyberpunk-neon",
        "subject_prefix": "",
        "subject_suffix": ", neon-lit, glowing edges, high contrast, futuristic",
        "style": "cyberpunk neon art, blade runner aesthetic, glowing neon lights, dark city night, vibrant magenta and cyan glow, rim lighting, reflective surfaces, atmospheric haze, detailed digital painting",
        "negative": "daylight, pastel, flat lighting, washed out, low contrast, pixel art, lowres, blurry, deformed, text, watermark, signature",
        "checkpoint": "IMG_dreamshaper_8.safetensors",
        "lora": None,
        "lora_strength": 1.0,
        "size": 512,
        "transparent": False,
        "post": {},
        "notes": (
            "Sin LoRA: no hay un LoRA cyberpunk gratis instalado y no se descargo ninguno "
            "gated/de pago. El neón lo da el prompt (glow magenta/cyan, rim lighting, ciudad "
            "nocturna) sobre IMG_dreamshaper_8 (SD1.5), que rinde bien en este registro. Sin post: "
            "el brillo y los gradientes son la identidad del estilo; pixelizar los destruiria. "
            "transparent=False para conservar el ambiente neón del fondo."
        ),
    },
    # Low-poly flat: estética PS1/PSX y arte 3D minimalista — facetas geométricas, flat
    # shading sin gradientes suaves, pocos polígonos, colores planos. PROMPT puro sobre
    # SD1.5: el LoRA 3d_render_redmond empuja a render fotorrealista (lo contrario de
    # low-poly), asi que se evita a propósito. Sin post (es flat shading limpio, no grid).
    "low-poly-flat": {
        "name": "low-poly-flat",
        "subject_prefix": "",
        "subject_suffix": ", low poly, faceted, flat shaded, geometric",
        "style": "low poly 3d art, flat shading, faceted geometry, minimal polygons, PS1 PSX aesthetic, clean solid colors, isometric game asset, no gradients, crisp facets",
        "negative": "photorealistic, smooth shading, soft gradient, high detail, realistic texture, blurry, noisy, pixel art, deformed, text, watermark, signature",
        "checkpoint": "IMG_dreamshaper_8.safetensors",
        "lora": None,
        "lora_strength": 1.0,
        "size": 512,
        "transparent": True,
        "post": {},
        "notes": (
            "Sin LoRA a propósito: SD15_3d_render_redmond (instalado) empuja a render "
            "fotorrealista, lo OPUESTO a low-poly. El look faceteado/flat lo da el prompt "
            "(low poly, faceted, flat shading, PS1) sobre IMG_dreamshaper_8 (SD1.5). Sin post: el "
            "flat shading es limpio de por sí, no necesita pixelize. transparent=True porque un "
            "asset low-poly suele ir recortado sobre el juego (silueta sólida bien definida)."
        ),
    },
    # Cartoon cel-shaded: dibujos animados / anime con sombreado plano por celdas, líneas
    # negras gruesas, colores saturados y planos (look toon/Borderlands/Zelda Wind Waker).
    # Usa SD15_anime_style_box (LoRA gratis ya instalado) a fuerza media + prompt cel-shaded.
    # Sin post (es ilustración vectorial limpia, no pixelart).
    "cartoon-cel-shaded": {
        "name": "cartoon-cel-shaded",
        "subject_prefix": "",
        "subject_suffix": ", cel shaded, bold outlines, flat colors, cartoon",
        "style": "cartoon cel-shaded art, bold black outlines, flat color fills, hard cel shadows, vibrant saturated colors, clean vector look, anime toon shading, comic style",
        "negative": "photorealistic, soft shading, gradient, realistic texture, painterly, blurry, noisy, pixel art, lowres, grainy, deformed, text, watermark, signature",
        "checkpoint": "IMG_dreamshaper_8.safetensors",
        "lora": "SD15_anime_style_box.safetensors",
        "lora_strength": 0.7,
        "size": 512,
        "transparent": True,
        "post": {},
        "notes": (
            "SD15_anime_style_box.safetensors (gratis, ya instalado en /mnt/2tb) a strength 0.7 "
            "empuja el toon/anime; el prompt sella el cel-shading (outlines negros gruesos, "
            "sombras duras por celdas, colores planos saturados). SD1.5 (IMG_dreamshaper_8). Sin "
            "post: el look vectorial limpio no necesita pixelize. transparent=True para recortar "
            "la silueta del personaje/objeto cartoon sobre el juego."
        ),
    },
}


def comfyui_get_gamedev_style_preset(name: str | None = None) -> dict:
    """Devuelve la receta de un style preset gamedev, o el catalogo si name es None.

    Args:
        name: identificador del estilo ("gameboy", "ghibli", "pixel-art-retro",
            "cyberpunk-neon", "low-poly-flat", "cartoon-cel-shaded"). Si es
            None (o cadena vacia), devuelve el catalogo de nombres disponibles en vez de
            una receta concreta (discovery). Insensible a mayusculas y a '_' vs '-'.

    Returns:
        - Si name es None/"": dict {"names": list[str], "count": int} con el catalogo.
        - Si name es un estilo valido: copia profunda de su receta (ver el modulo para los
          campos). Es una COPIA — mutarla no afecta al registro interno.

    Raises:
        ValueError: si name no es None pero no corresponde a ningun preset. El mensaje
            lista los nombres disponibles.
    """
    if name is None or (isinstance(name, str) and not name.strip()):
        names = sorted(_PRESETS)
        return {"names": names, "count": len(names)}

    key = name.strip().lower().replace("_", "-")
    if key not in _PRESETS:
        raise ValueError(
            f"comfyui_get_gamedev_style_preset: estilo desconocido {name!r}. "
            f"Disponibles: {sorted(_PRESETS)}"
        )
    return copy.deepcopy(_PRESETS[key])


if __name__ == "__main__":
    import json

    print(json.dumps(comfyui_get_gamedev_style_preset(), indent=2))
    print(json.dumps(comfyui_get_gamedev_style_preset("gameboy"), indent=2, ensure_ascii=False))
