# -*- coding: utf-8 -*-
"""
ComfyUI UI-workflow (.json)  ->  API-prompt (.json) donusturucu.

Kullanim ornegi:
  python tools/comfy-workflow-to-api.py --workflow "FikoYT-img2-3d.json" ^
      --out public/comfy/FikoYT-img2-3d.api.json

--workflow: yerel dosya yolu VEYA ComfyUI userdata/workflows icindeki ad.
object_info calisan ComfyUI sunucusundan cekilir (widget sirasi icin sart).
"""
import argparse, json, os, urllib.parse, urllib.request

WIDGET_SCALARS = {"INT", "FLOAT", "STRING", "BOOLEAN", "COMBO",
                  # link ile beslenmeyen, frontend'de widget olarak duran ozel tipler
                  "LOAD_3D", "LOAD_3D_ANIMATION", "IMAGEUPLOAD", "VIDEOUPLOAD", "AUDIOUPLOAD"}


def http_json(url):
    with urllib.request.urlopen(url, timeout=120) as r:
        return json.loads(r.read().decode("utf-8"))


def http_text(url):
    with urllib.request.urlopen(url, timeout=120) as r:
        return r.read().decode("utf-8")


def widget_names(spec):
    """object_info node spec -> sirali widget input adlari."""
    out = []
    inp = spec.get("input", {}) or {}
    for bucket in ("required", "optional"):
        for name, cfg in (inp.get(bucket) or {}).items():
            if not isinstance(cfg, (list, tuple)) or not cfg:
                continue
            t = cfg[0]
            opts = cfg[1] if len(cfg) > 1 and isinstance(cfg[1], dict) else {}
            is_widget = isinstance(t, list) or (isinstance(t, str) and t in WIDGET_SCALARS)
            if not is_widget:
                continue
            out.append(name)
            # seed/control_after_generate ekstra bir widget degeri tuketir
            if opts.get("control_after_generate") or name in ("seed", "noise_seed"):
                out.append("__control_after_generate__")
    return out


def convert(ui, object_info):
    nodes = ui.get("nodes", [])
    prompt = {}
    atlanan = []
    for n in nodes:
        cls = n.get("type")
        spec = object_info.get(cls)
        if spec is None:
            atlanan.append("%s (%s)" % (n.get("id"), cls))
            continue
        mode = n.get("mode", 0)
        if mode in (2, 4):          # muted / bypass
            atlanan.append("%s (%s) mode=%s" % (n.get("id"), cls, mode))
            continue

        inputs = {}
        # 1) widget degerleri (sirali)
        wv = n.get("widgets_values")
        if isinstance(wv, dict):
            for k, v in wv.items():
                inputs[k] = v
        elif isinstance(wv, list):
            # Widget sirasi: once node.inputs icindeki `widget` girdileri (frontend'in
            # kendi sirasi — dinamik combo alt-alanlarini da icerir), yoksa object_info.
            adlar = [i["name"] for i in (n.get("inputs") or []) if i.get("widget")]
            if not adlar:
                adlar = [x for x in widget_names(spec) if x != "__control_after_generate__"]
            i = 0
            for name in adlar:
                if i >= len(wv):
                    break
                inputs[name] = wv[i]
                i += 1
                # seed/control_after_generate ekstra bir deger tuketir
                if i < len(wv) and isinstance(wv[i], str) and \
                   wv[i] in ("fixed", "increment", "decrement", "randomize"):
                    i += 1

        # 2) link'ler (widget degerini ezer)
        for ip in (n.get("inputs") or []):
            lid = ip.get("link")
            if lid is None:
                continue
            src = LINKS.get(lid)
            if src is None:
                continue
            inputs[ip.get("name")] = [str(src[0]), src[1]]

        node_out = {"class_type": cls, "inputs": inputs}
        title = n.get("title")
        if title:
            node_out["_meta"] = {"title": title}
        prompt[str(n.get("id"))] = node_out
    return prompt, atlanan


LINKS = {}


def main():
    global LINKS
    ap = argparse.ArgumentParser()
    ap.add_argument("--comfy", default="http://127.0.0.1:8188")
    ap.add_argument("--workflow", required=True)
    ap.add_argument("--out", required=True)
    a = ap.parse_args()

    if os.path.isfile(a.workflow):
        ui = json.load(open(a.workflow, "r", encoding="utf-8"))
    else:
        q = urllib.parse.quote("workflows/" + a.workflow, safe="")
        ui = json.loads(http_text(a.comfy.rstrip("/") + "/api/userdata/" + q))

    # link tablosu: [id, origin_node, origin_slot, target_node, target_slot, type]
    for l in (ui.get("links") or []):
        if isinstance(l, list) and len(l) >= 3:
            LINKS[l[0]] = (l[1], l[2])
        elif isinstance(l, dict):
            LINKS[l["id"]] = (l["origin_id"], l["origin_slot"])

    oi = http_json(a.comfy.rstrip("/") + "/api/object_info")
    prompt, atlanan = convert(ui, oi)

    os.makedirs(os.path.dirname(os.path.abspath(a.out)), exist_ok=True)
    with open(a.out, "w", encoding="utf-8") as f:
        json.dump(prompt, f, ensure_ascii=False, indent=1)

    print("OK  node: %d  atlanan: %d -> %s" % (len(prompt), len(atlanan), a.out))
    for x in atlanan:
        print("   atlandi:", x)


if __name__ == "__main__":
    main()
