"""Lista los workflow templates oficiales que trae el paquete comfyui-workflow-templates.

Funcion impura: lee disco (los .json de los templates instalados) ejecutando la
API oficial del paquete dentro del interprete de ComfyUI.

ComfyUI 0.26+ distribuye los templates oficiales (los del menu "Browse Templates"
del frontend) en el paquete pip `comfyui-workflow-templates`, que desde la 0.10.x es
un meta-paquete multi-bundle: ya NO expone una carpeta `templates/` unica, sino una
API en `comfyui_workflow_templates_core` (`load_manifest`, `iter_templates`,
`get_asset_path`). Cada template es un grafo de nodos en formato UI (nodes/links con
posiciones), agrupado en uno de cuatro bundles: media-api, media-image, media-video,
media-other.

Como el paquete vive en el venv de ComfyUI (no en el del registry), esta funcion no
lo importa directamente: localiza el interprete de ComfyUI y le pasa un script que usa
la API oficial y vuelca el catalogo como JSON. Asi es robusta ante cambios de la
estructura interna del paquete.
"""
import json
import os
import subprocess
import sys


# Script que corre DENTRO del python de ComfyUI. Usa la API oficial del paquete y
# vuelca el catalogo (metadata + node_types por template) como una linea JSON.
_DUMP_SCRIPT = r"""
import json, sys, re
try:
    import comfyui_workflow_templates_core as core
except Exception as exc:
    print(json.dumps({"__err__": "import", "msg": str(exc)}))
    sys.exit(0)

_UUID_RE = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")

def _collect_types(graph):
    # Recoge class_types reales: aplana los subgrafos de definitions y descarta los
    # type que son UUID (instancias de subgraph, cuyo contenido real ya se incluye).
    types = set()
    if isinstance(graph, dict) and isinstance(graph.get("nodes"), list):
        for n in graph["nodes"]:
            if isinstance(n, dict) and n.get("type") and not _UUID_RE.match(str(n["type"])):
                types.add(n["type"])
        defs = graph.get("definitions")
        if isinstance(defs, dict) and isinstance(defs.get("subgraphs"), list):
            for sg in defs["subgraphs"]:
                for n in (sg.get("nodes") or []) if isinstance(sg, dict) else []:
                    if isinstance(n, dict) and n.get("type") and not _UUID_RE.match(str(n["type"])):
                        types.add(n["type"])
        return len(graph["nodes"]), sorted(types)
    if isinstance(graph, dict):  # API format
        for v in graph.values():
            if isinstance(v, dict) and v.get("class_type"):
                types.add(v["class_type"])
        if types:
            return len(graph), sorted(types)
    return 0, []

WITH_NODES = {with_nodes}
m = core.load_manifest()
try:
    import importlib.metadata as _md
    pkg_version = _md.version("comfyui-workflow-templates")
except Exception:
    pkg_version = ""

out = []
for tid, entry in m.templates.items():
    json_asset = next(
        (a.filename for a in entry.assets if a.filename.endswith(".json")), None
    )
    path = core.get_asset_path(tid, json_asset) if json_asset else ""
    rec = {
        "name": tid,
        "bundle": entry.bundle,
        "category": entry.bundle,
        "version": entry.version,
        "path": path,
        "n_nodes": 0,
        "node_types": [],
    }
    rec["is_workflow"] = False
    if path:
        try:
            with open(path, encoding="utf-8") as fh:
                graph = json.load(fh)
            n_nodes, node_types = _collect_types(graph)
            is_api = isinstance(graph, dict) and any(
                isinstance(v, dict) and v.get("class_type") for v in graph.values()
            )
            rec["is_workflow"] = bool(
                (isinstance(graph, dict) and isinstance(graph.get("nodes"), list) and graph["nodes"])
                or is_api
            )
            rec["n_nodes"] = n_nodes
            if WITH_NODES:
                rec["node_types"] = node_types
        except Exception:
            pass
    out.append(rec)

print(json.dumps({"package_version": pkg_version, "templates": out}))
"""


def _find_comfyui_python(explicit: str | None) -> str | None:
    """Devuelve la ruta a un interprete de ComfyUI que tenga el paquete instalado.

    Orden de busqueda: argumento explicito -> env COMFYUI_PYTHON -> candidatos
    habituales (~/ComfyUI/.venv, ~/ComfyUI/venv) -> el python actual. Devuelve None
    si ninguno existe en disco.
    """
    candidates = []
    if explicit:
        candidates.append(os.path.expanduser(explicit))
    env = os.environ.get("COMFYUI_PYTHON")
    if env:
        candidates.append(os.path.expanduser(env))
    candidates += [
        os.path.expanduser("~/ComfyUI/.venv/bin/python"),
        os.path.expanduser("~/ComfyUI/venv/bin/python"),
        os.path.expanduser("~/comfyui/.venv/bin/python"),
        sys.executable,
    ]
    for c in candidates:
        if c and os.path.isfile(c):
            return c
    return None


def comfyui_list_templates(
    comfyui_python: str | None = None,
    bundle: str | None = None,
    name_filter: str | None = None,
    with_nodes: bool = True,
    workflows_only: bool = True,
    limit: int = 0,
) -> dict:
    """Lista los templates oficiales de ComfyUI con su grafo de nodos.

    Args:
        comfyui_python: ruta al interprete python de ComfyUI que tiene instalado
            el paquete comfyui-workflow-templates. Si None, se autodetecta (env
            COMFYUI_PYTHON o ~/ComfyUI/.venv/bin/python).
        bundle: si se da, filtra por bundle exacto ("media-api", "media-image",
            "media-video", "media-other").
        name_filter: si se da, filtra a templates cuyo nombre contenga esta
            subcadena (case-insensitive).
        with_nodes: si True (default) incluye node_types en cada registro. Si
            False los omite (registros mas ligeros).
        workflows_only: si True (default) excluye entradas que no son grafos de
            workflow (ficheros index*/localizacion del paquete).
        limit: si > 0, trunca la lista a los primeros N tras filtrar.

    Returns:
        dict {ok, count, package_version, templates, error}:
          - templates: lista de {name, category, bundle, version, path, n_nodes,
            node_types} ordenada por name.
          - count: numero de templates devueltos (tras filtros y limit).
        Nunca lanza: cualquier fallo (paquete ausente, interprete no hallado)
        devuelve ok=False con un error legible.
    """
    py = _find_comfyui_python(comfyui_python)
    if not py:
        return {
            "ok": False,
            "count": 0,
            "package_version": "",
            "templates": [],
            "error": (
                "no se encontro un interprete de ComfyUI. Pasa comfyui_python=... "
                "o define COMFYUI_PYTHON. El paquete se instala con: "
                "pip install comfyui-workflow-templates"
            ),
        }

    script = _DUMP_SCRIPT.replace("{with_nodes}", "True" if with_nodes else "False")
    try:
        proc = subprocess.run(
            [py, "-c", script],
            capture_output=True,
            text=True,
            timeout=120,
        )
    except Exception as exc:  # noqa: BLE001
        return {
            "ok": False,
            "count": 0,
            "package_version": "",
            "templates": [],
            "error": f"fallo al ejecutar el interprete de ComfyUI ({py}): {exc}",
        }

    if proc.returncode != 0:
        return {
            "ok": False,
            "count": 0,
            "package_version": "",
            "templates": [],
            "error": f"el interprete de ComfyUI fallo: {proc.stderr.strip()[:500]}",
        }

    try:
        data = json.loads(proc.stdout.strip().splitlines()[-1])
    except Exception as exc:  # noqa: BLE001
        return {
            "ok": False,
            "count": 0,
            "package_version": "",
            "templates": [],
            "error": f"salida no parseable del interprete de ComfyUI: {exc}",
        }

    if data.get("__err__") == "import":
        return {
            "ok": False,
            "count": 0,
            "package_version": "",
            "templates": [],
            "error": (
                "el paquete comfyui-workflow-templates no esta instalado en "
                f"{py} ({data.get('msg', '')}). Instalalo con: "
                "pip install comfyui-workflow-templates"
            ),
        }

    templates = data.get("templates", [])
    if workflows_only:
        templates = [t for t in templates if t.get("is_workflow")]
    if bundle:
        templates = [t for t in templates if t.get("bundle") == bundle]
    if name_filter:
        nf = name_filter.lower()
        templates = [t for t in templates if nf in t.get("name", "").lower()]
    templates.sort(key=lambda t: t.get("name", ""))
    if limit and limit > 0:
        templates = templates[:limit]

    return {
        "ok": True,
        "count": len(templates),
        "package_version": data.get("package_version", ""),
        "templates": templates,
        "error": "",
    }


if __name__ == "__main__":
    import argparse

    ap = argparse.ArgumentParser(description="Lista templates oficiales de ComfyUI")
    ap.add_argument("--comfyui-python", default=None)
    ap.add_argument("--bundle", default=None)
    ap.add_argument("--name-filter", default=None)
    ap.add_argument("--no-nodes", action="store_true", help="omite node_types")
    ap.add_argument("--all", action="store_true", help="incluye entradas no-workflow (index*)")
    ap.add_argument("--limit", type=int, default=0)
    ap.add_argument("--full", action="store_true", help="dump completo (todos los node_types)")
    args = ap.parse_args()

    res = comfyui_list_templates(
        args.comfyui_python,
        bundle=args.bundle,
        name_filter=args.name_filter,
        with_nodes=not args.no_nodes,
        workflows_only=not args.all,
        limit=args.limit,
    )
    if args.full or not res["ok"]:
        print(json.dumps(res, indent=2, ensure_ascii=False))
    else:
        print(
            json.dumps(
                {
                    "ok": res["ok"],
                    "count": res["count"],
                    "package_version": res["package_version"],
                    "sample": res["templates"][:15],
                },
                indent=2,
                ensure_ascii=False,
            )
        )
