"""Edita en vivo el valor de un widget de un nodo del grafo de ComfyUI via CDP.

Localiza un nodo en `app.graph._nodes` por su tipo (comfyClass), su id o su
titulo, y asigna el valor del widget cuyo `name` coincide. Cubre tanto widgets
numericos (steps, cfg, seed del KSampler) como de texto (el `text` de un
CLIPTextEncode). Tras asignar `widget.value` invoca `widget.callback` si existe
para propagar el cambio y marca el canvas dirty. Compone cdp_eval.

Funcion impura: hace red (CDP WebSocket) y muta el grafo de la UI.
"""
import json

try:  # ejecucion directa del archivo / fn run (browser/ en sys.path[0])
    from cdp_eval import cdp_eval
except ImportError:  # importado como paquete (sys.path = python/functions)
    from browser.cdp_eval import cdp_eval


def comfyui_set_node_widget_ui(
    node: str,
    widget_name: str,
    value,
    *,
    match: str = "type",
    port: int = 9222,
    server_url_substr: str = "8188",
    timeout_s: float = 15.0,
) -> dict:
    """Asigna el valor de un widget de un nodo del grafo en vivo.

    Args:
        node: identificador del nodo a localizar, interpretado segun `match`.
        widget_name: nombre del widget a editar (ej. "text", "steps", "seed",
            "cfg", "sampler_name").
        value: nuevo valor (str, int, float o bool). Se serializa a JSON.
        match: criterio de busqueda del nodo. "type" (por comfyClass/type, ej.
            "CLIPTextEncode" o "KSampler"), "id" (por n.id) o "title" (por el
            titulo visible del nodo). Default "type".
        port: puerto de remote debugging del Chrome diario. Default 9222.
        server_url_substr: substring de la URL de la pestana de ComfyUI.
        timeout_s: timeout de la conexion CDP en segundos.

    Returns:
        dict {ok, matched_nodes (int), set (bool), old_value, new_value, error}.
        Si `match="type"` produce varios nodos, actua sobre el primero y reporta
        cuantos coincidieron en matched_nodes.
    """
    expr = (
        "(function(){"
        "  if(!window.app || !app.graph) return {matched_nodes:0, set:false, error:'window.app.graph no disponible'};"
        "  var nodes = app.graph._nodes || [];"
        f"  var key = {json.dumps(match)};"
        f"  var target = {json.dumps(node)};"
        f"  var wname = {json.dumps(widget_name)};"
        f"  var nval = {json.dumps(value)};"
        "  var matches = nodes.filter(function(n){"
        "    if(key==='id') return String(n.id)===String(target);"
        "    if(key==='title') return n.title===target;"
        "    return (n.comfyClass||n.type)===target;"
        "  });"
        "  if(matches.length===0) return {matched_nodes:0, set:false, error:'sin nodo que matchee '+key+'='+target};"
        "  var n = matches[0];"
        "  var w = (n.widgets||[]).find(function(x){return x.name===wname;});"
        "  if(!w) return {matched_nodes:matches.length, set:false, error:'el nodo no tiene widget \"'+wname+'\"'};"
        "  var old = w.value;"
        "  w.value = nval;"
        "  if(typeof w.callback==='function'){ try{ w.callback(nval); }catch(e){} }"
        "  if(typeof app.graph.setDirtyCanvas==='function') app.graph.setDirtyCanvas(true,true);"
        "  return {matched_nodes:matches.length, set:true, old_value:old, new_value:w.value, error:''};"
        "})()"
    )
    r = cdp_eval(
        expr,
        port=port,
        target_url_substr=server_url_substr,
        await_promise=False,
        timeout_s=timeout_s,
    )
    if not r["ok"]:
        return {
            "ok": False,
            "matched_nodes": 0,
            "set": False,
            "old_value": None,
            "new_value": None,
            "error": r["error"],
        }
    val = r["value"] or {}
    return {
        "ok": bool(val.get("set")),
        "matched_nodes": val.get("matched_nodes", 0),
        "set": bool(val.get("set")),
        "old_value": val.get("old_value"),
        "new_value": val.get("new_value"),
        "error": val.get("error", ""),
    }


if __name__ == "__main__":
    out = comfyui_set_node_widget_ui(
        "KSampler", "steps", 25, match="type"
    )
    print(json.dumps(out, ensure_ascii=False, indent=2))
