"""Construye el workflow ComfyUI de UN mapa de mundo / mapa de nivel ilustrado (API format).

Mapa de aventura estilo fantasy / cartografia (un continente, una region, una
mazmorra, un reino) en vista cenital cartografica, pensado para la PANTALLA DE MAPA
del juego: la ilustracion que se ve al abrir el mapa del mundo, seleccionar nivel o
consultar la region actual. Es el builder hermano de
comfyui_build_splash_art_workflow / comfyui_build_parallax_background_workflow: mismo
patron (PURO, dict API format) que compone funciones existentes del registry, no
reescribe el grafo.

IMPORTANTE — alcance del builder: genera SOLO la ILUSTRACION del mapa (la lamina
cartografica). Las marcas de ubicacion interactivas, los iconos de ciudad/punto de
interes pinchables, las rutas dinamicas, el "estas aqui" y cualquier texto/label
fiable los compone el motor de juego SOBRE la ilustracion resultante — la difusion
puede dibujar marcas y letras DECORATIVAS (parte del look cartografico) pero NO
garantiza ortografia ni posiciones utilizables como datos. El prompt scaffold empuja a
"top-down cartographic view, illustrated game world map, labeled regions, decorative
border" para lograr el aspecto de mapa; el negativo por defecto rechaza la perspectiva
3D, la foto y los personajes en primer plano para mantener la vista cenital plana.

Cableado segun los argumentos:

    [hires] CheckpointLoaderSimple -> [LoraLoader si lora] -> KSampler (base) ->
            VAEDecode -> UpscaleModelLoader + UltimateSDUpscale -> SaveImage
    [plano] CheckpointLoaderSimple -> [LoraLoader si lora] -> KSampler ->
            VAEDecode -> SaveImage

Compone:
  - comfyui_build_hires_fix_workflow (si hires) -> base + 2a pasada de detalle por
    tiles (UltimateSDUpscale + Remacri); el mapa luce el detalle fino de costas,
    relieve y ornamentos que se aprecia al ampliar la lamina.
  - comfyui_build_txt2img_workflow (si no hires) -> base txt2img simple.
  - comfyui_inject_lora -> LoRA de estilo opcional (cartografia antigua / fantasy /
    pergamino / tinta) para fijar la identidad visual del mapa del juego.

Por que cuadrado por defecto (768x768): una pantalla de mapa suele encuadrar una
region completa y la cartografia clasica tiende al formato cuadrado/proporcionado;
768x768 va holgado en 8GB lowvram. Para un mundo apaisado (banda continental ancha)
sube width; para un mapa en columna (torre/mazmorra vertical) sube height.

Por que hires opcional y False por defecto: un mapa se lee a escala media y el
scaffold ya da bastante detalle; hires=True anade detalle real (costas, montañas,
ornamento del borde) a costa de tiempo/VRAM. En 8GB lowvram con SDXL puede ser
pesado: baja a SD1.5 (IMG_dreamshaper_8) o deja hires=False si la GPU se queda corta (OOM).

class_types/inputs verificados contra /object_info del servidor (8GB lowvram) a
traves de los builders que compone (CheckpointLoaderSimple, CLIPTextEncode,
EmptyLatentImage, KSampler, VAEDecode, SaveImage, LoraLoader, UpscaleModelLoader,
UltimateSDUpscale).

Funcion pura: sin red, sin I/O. No muta dicts de entrada (los builders/inyectores
que compone trabajan sobre copias). Determinista para los mismos argumentos.
"""
from __future__ import annotations

import os
import sys

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

# Negativo por defecto pensado para una lamina cartografica: vista cenital plana,
# ilustrada, SIN la perspectiva 3D / foto / primer plano de personajes que romperian
# el aspecto de mapa. NO rechaza "text": un mapa lleva labels/ornamentos decorativos
# como parte del look (aunque la difusion no garantice su ortografia).
_MAP_NEGATIVE = (
    "photo, photograph, 3d render, realistic perspective, vanishing point, "
    "first person view, close-up, character, person, people, portrait, "
    "blurry, lowres, deformed, jpeg artifacts, low quality, watermark, signature"
)


def comfyui_build_world_map_workflow(
    region: str,
    *,
    map_style: str = "fantasy cartography, aged parchment",
    checkpoint: str = "IMG_juggernaut_xl_v11.safetensors",
    width: int = 768,
    height: int = 768,
    hires: bool = False,
    seed: int = 0,
    lora: str | None = None,
    lora_strength: float = 1.0,
    negative: str | None = None,
    steps: int = 28,
    cfg: float = 7.0,
    sampler_name: str = "dpmpp_2m",
    scheduler: str = "karras",
    upscale_by: float = 1.5,
    hires_denoise: float = 0.4,
    upscale_model: str = "4x_foolhardy_Remacri.pth",
    filename_prefix: str = "world_map",
) -> dict:
    """Construye el dict (API format) del workflow de un mapa de mundo/nivel ilustrado.

    Args:
        region: descripcion de la region a cartografiar (ej. "a fantasy kingdom with
            mountains, forests and a coastline", "a dark dungeon with branching
            corridors", "a desert continent with an oasis and ruins"). Se inserta en
            un prompt scaffold de mapa cartografico cenital. No puede estar vacio.
        map_style: tratamiento cartografico que define el look del mapa (ej. "fantasy
            cartography, aged parchment", "hand-drawn ink map", "old nautical chart",
            "painted region map, vibrant"). Pasa el MISMO map_style + checkpoint +
            (lora) a varios mapas para coherencia visual del juego. keyword-only.
        checkpoint: checkpoint del servidor. 'IMG_juggernaut_xl_v11.safetensors' (SDXL,
            mejor detalle de costa/relieve/ornamento) por defecto; en 8GB lowvram con
            hires puede ser pesado: si la GPU se queda corta, usa
            'IMG_dreamshaper_8.safetensors' (SD1.5) y/o hires=False. keyword-only.
        width: ancho del lienzo en px. 768 por defecto (cuadrado). Para un mundo
            apaisado (banda continental ancha) sube width. keyword-only.
        height: alto del lienzo en px. 768 por defecto (cuadrado). Para un mapa en
            columna (torre/mazmorra vertical) sube height. keyword-only.
        hires: si True encadena la 2a pasada de detalle (UltimateSDUpscale + Remacri,
            re-difusion por tiles) sobre la base para una lamina mas detallada
            (costas, montañas, ornamento del borde). False (defecto) deja la imagen
            tal cual sale del VAEDecode: mas rapido para iterar. keyword-only.
        seed: semilla del KSampler (y de la pasada hires). Misma seed + misma region
            -> mismo mapa. keyword-only.
        lora: LoRA de estilo opcional en models/loras (ej. 'cartography_sd15.safe-
            tensors', 'SDXL_old_map.safetensors'). None = sin LoRA. Encadena la
            identidad visual del mapa del juego. keyword-only.
        lora_strength: fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0].
            keyword-only.
        negative: prompt negativo. None usa el negativo por defecto pensado para una
            lamina cartografica (vista cenital plana, sin perspectiva 3D / foto /
            personaje en primer plano). keyword-only.
        steps: pasos del KSampler (y de la pasada hires). keyword-only.
        cfg: CFG del KSampler (y de la pasada hires). keyword-only.
        sampler_name: sampler del KSampler. keyword-only.
        scheduler: scheduler del KSampler. keyword-only.
        upscale_by: factor de ampliacion de la pasada hires sobre la base (1.5 ->
            768x768 pasa a 1152x1152). Solo se usa si hires=True. keyword-only.
        hires_denoise: fuerza de re-difusion de la pasada hires (0.4 por defecto:
            anade detalle sin alterar el trazado del mapa). Solo si hires=True.
            keyword-only.
        upscale_model: modelo de upscale en models/upscale_models/ que usa la pasada
            hires ('4x_foolhardy_Remacri.pth'). Solo si hires=True. keyword-only.
        filename_prefix: prefijo del PNG generado en output/. keyword-only.

    Returns:
        dict en API format listo para comfyui_submit_workflow: base (hires-fix si
        hires, txt2img si no) con prompt scaffold de mapa ('map of {region},
        {map_style}, top-down cartographic view, illustrated game world map, labeled
        regions, decorative border, ...') + LoRA de estilo opcional. Es UNA lamina;
        un atlas coherente -> llamar por region con el mismo map_style/checkpoint/
        (lora). Las marcas interactivas, los iconos pinchables y el texto fiable los
        pone el motor, no este workflow.

    Raises:
        ValueError: si region esta vacio, o si los builders/inyectores que compone no
            encuentran los nodos donde enganchar (propagado).
    """
    if not region or not region.strip():
        raise ValueError(
            "comfyui_build_world_map_workflow: 'region' no puede estar vacio"
        )

    region = region.strip()
    lora_strength = max(0.0, min(2.0, float(lora_strength)))
    neg = _MAP_NEGATIVE if negative is None else negative

    # Prompt scaffold de mapa: lamina cartografica cenital ilustrada con regiones
    # rotuladas y borde ornamental. 'top-down cartographic view' fuerza la vista de
    # mapa; 'no people' refuerza que es la lamina, no una escena.
    positive = (
        f"map of {region}, {map_style}, top-down cartographic view, "
        "illustrated game world map, labeled regions, decorative border, "
        "detailed terrain, compass rose, fantasy atlas, no people, high quality"
    )

    if hires:
        from ml.comfyui_build_hires_fix_workflow import (
            comfyui_build_hires_fix_workflow,
        )

        wf = comfyui_build_hires_fix_workflow(
            checkpoint,
            positive,
            neg,
            first_pass=(width, height),
            upscale_by=upscale_by,
            denoise=hires_denoise,
            steps=steps,
            cfg=cfg,
            seed=seed,
            upscale_model=upscale_model,
            sampler_name=sampler_name,
            scheduler=scheduler,
            filename_prefix=filename_prefix,
        )
    else:
        from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow

        wf = comfyui_build_txt2img_workflow(
            checkpoint,
            positive,
            neg,
            steps=steps,
            cfg=cfg,
            width=width,
            height=height,
            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
        )

    return wf


if __name__ == "__main__":
    import json

    wf = comfyui_build_world_map_workflow(
        "a fantasy kingdom with mountains, forests and a coastline",
        map_style="fantasy cartography, aged parchment",
        checkpoint="IMG_dreamshaper_8.safetensors",
        hires=False,
        seed=7,
    )
    print(
        json.dumps(
            {
                "nodes": list(wf),
                "classes": sorted({n["class_type"] for n in wf.values()}),
            },
            indent=2,
        )
    )
