#!/usr/bin/env python3
"""H3 ref2va direct ComfyUI API - REF2VA model + correct CLIP (qwen3vl_32b), no Turbo LoRA."""
import requests, json, os, time, sys

COMFYUI = "http://localhost:8188"
KAP = "http://localhost:10588"

# ── Workflow builder ──
def build_ref2va_workflow(prompt, width, height, length, ref_image_files):
    """
    Build a ref2va workflow using:
    - REF2VA model: minimax_h3_ref2va_pruned_int8_convrot.safetensors
    - CLIP: qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors (NOT gemma!)
    - No Turbo LoRA (incompatible with REF2VA)
    - 20 steps standard
    """
    wf = {
        "10": {
            "class_type": "UNETLoader",
            "inputs": {
                "unet_name": "minimax_h3_ref2va_pruned_int8_convrot.safetensors",
                "weight_dtype": "default"
            }
        },
        "11": {
            "class_type": "CLIPLoader",
            "inputs": {
                "clip_name": "qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors",
                "type": "minimax"
            }
        },
        "12": {
            "class_type": "VAELoader",
            "inputs": {
                "vae_name": "minimax_h3_video_vae_fp16.safetensors"
            }
        },
        "13": {
            "class_type": "VAELoader",
            "inputs": {
                "vae_name": "minimax_h3_audio_vae_fp32.safetensors"
            }
        },
        # ref_image LoadImage nodes: 14, 140, 141, ...
        "20": {
            "class_type": "MiniMaxH3ReferenceToVideo",
            "inputs": {
                "clip": ["11", 0],
                "vae": ["12", 0],
                "audio_vae": ["13", 0],
                "prompt": prompt,
                "width": width,
                "height": height,
                "length": length,
                "ref_image_size": "match",
                # ref_image_N connections added below
            }
        },
        "30": {
            "class_type": "MiniMaxH3SigmaShift",
            "inputs": {
                "model": ["10", 0],
                "shift_video": 12.0,
                "shift_audio": 3.0
            }
        },
        "90": {
            "class_type": "MiniMaxH3ReferenceToVideo",
            "inputs": {
                "clip": ["11", 0],
                "vae": ["12", 0],
                "audio_vae": ["13", 0],
                "prompt": "blurry, low quality, distorted, watermark",
                "width": width,
                "height": height,
                "length": length,
                "ref_image_size": "match"
            }
        },
        "50": {
            "class_type": "KSampler",
            "inputs": {
                "model": ["30", 0],
                "positive": ["20", 0],
                "negative": ["90", 0],
                "latent_image": ["20", 1],
                "seed": 42,
                "steps": 20,
                "cfg": 7.0,
                "sampler_name": "euler",
                "scheduler": "normal",
                "denoise": 1.0
            }
        },
        "100": {
            "class_type": "VAEDecode",
            "inputs": {
                "samples": ["50", 0],
                "vae": ["12", 0]
            }
        },
        "110": {
            "class_type": "SaveAnimatedWEBP",
            "inputs": {
                "images": ["100", 0],
                "filename_prefix": "h3_ref2va",
                "fps": 24.0,
                "lossless": False,
                "quality": 90,
                "method": "default"
            }
        }
    }

    # Add ref_image LoadImage nodes and connect to node 20
    for i, fname in enumerate(ref_image_files):
        node_id = str(14 + i * 10)  # 14, 24, 34, ...
        wf[node_id] = {
            "class_type": "LoadImage",
            "inputs": {
                "image": os.path.basename(fname)
            }
        }
        wf["20"]["inputs"][f"ref_image_{i+1}"] = [node_id, 0]

    return wf


def upload_image(filepath):
    """Upload image to ComfyUI input directory."""
    fname = os.path.basename(filepath)
    with open(filepath, "rb") as f:
        resp = requests.post(f"{COMFYUI}/upload/image", files={"image": f}, data={"overwrite": "true"})
    if resp.status_code != 200:
        print(f"  Upload failed: {resp.text}")
        return None
    return resp.json().get("name", fname)


def submit_and_wait(wf, timeout=600):
    """Submit workflow and poll for completion."""
    resp = requests.post(f"{COMFYUI}/prompt", json={"prompt": wf})
    r = resp.json()
    if resp.status_code != 200 or "node_errors" in r:
        print(f"  Validation error: {json.dumps(r, indent=2)[:500]}")
        return None
    pid = r.get("prompt_id")
    if not pid:
        print(f"  No prompt_id: {r}")
        return None

    print(f"  promptId: {pid}")
    start = time.time()
    while time.time() - start < timeout:
        time.sleep(5)
        s = requests.get(f"{COMFYUI}/history/{pid}").json()
        if pid in s:
            outputs = s[pid].get("outputs", {})
            status = s[pid].get("status", {})
            if status.get("completed"):
                for nid, out in outputs.items():
                    if "images" in out:
                        return out["images"][0]
                    if "gifs" in out:
                        return out["gifs"][0]
                return None
            elif status.get("status_str") == "error":
                msgs = status.get("messages", [])
                print(f"  ERROR: {msgs[:200] if msgs else 'unknown'}")
                return None
            elapsed = int(time.time() - start)
            print(f"  ...running ({elapsed}s)")
    print(f"  TIMEOUT after {timeout}s")
    return None


def download_output(item, output_dir):
    """Download output from ComfyUI."""
    fname = item["filename"]
    subfolder = item.get("subfolder", "")
    url = f"{COMFYUI}/view?filename={fname}&subfolder={subfolder}&type=output"
    resp = requests.get(url)
    if resp.status_code == 200:
        outpath = os.path.join(output_dir, fname)
        with open(outpath, "wb") as f:
            f.write(resp.content)
        return outpath
    return None


def main():
    base_dir = "/home/kai/文档/LTX/validition_v1"
    output_dir = "/home/kai/shared/h3_ref2va_results"
    os.makedirs(output_dir, exist_ok=True)

    cases = sorted([d for d in os.listdir(base_dir) if os.path.isdir(os.path.join(base_dir, d))])

    for case_id in cases:
        case_dir = os.path.join(base_dir, case_id)
        print(f"\n{'='*60}")
        print(f"Case {case_id}: ref2va (REF2VA model + qwen3vl CLIP)")

        # Load prompt
        prompt_file = os.path.join(case_dir, "prompt.txt")
        if not os.path.exists(prompt_file):
            prompt_file = os.path.join(case_dir, "prompt_nsfw.txt")
        if not os.path.exists(prompt_file):
            print(f"  No prompt file, skipping")
            continue
        with open(prompt_file) as f:
            prompt = f.read().strip()

        # Find images
        imgs = sorted([f for f in os.listdir(case_dir) if f.endswith((".png", ".jpg", ".webp"))])
        if not imgs:
            print(f"  No images, skipping")
            continue

        # Upload all images
        print(f"  Images: {imgs}")
        uploaded = []
        for img_name in imgs:
            path = os.path.join(case_dir, img_name)
            uploaded_name = upload_image(path)
            if uploaded_name:
                uploaded.append((uploaded_name, path))
        if not uploaded:
            print(f"  Upload failed, skipping")
            continue

        # Determine dimensions from workflow json if available
        wf_file = os.path.join(case_dir, "ltx_msr_workflow.json")
        width, height = 1344, 768  # default landscape
        if os.path.exists(wf_file):
            try:
                with open(wf_file) as f:
                    wf_data = json.load(f)
                for node in wf_data.values():
                    if node.get("class_type") == "EmptyLatentVideo" or "width" in node.get("inputs", {}):
                        w = node["inputs"].get("width")
                        h = node["inputs"].get("height")
                        if w and h:
                            width, height = w, h
                            break
            except:
                pass

        # Align to n%17==5
        length = 124  # ~5s default
        while length % 17 != 5:
            length += 1

        print(f"  Prompt: {len(prompt)} chars")
        print(f"  Dimensions: {width}x{height}, length={length}")
        print(f"  Refs: {len(uploaded)}")

        # Build workflow with uploaded filenames
        wf = build_ref2va_workflow(prompt, width, height, length, [u[0] for u in uploaded])

        # Submit
        print(f"  Submitting...")
        result = submit_and_wait(wf, timeout=600)

        if result:
            outpath = download_output(result, output_dir)
            if outpath:
                print(f"  ✅ DONE: {os.path.basename(outpath)}")
            else:
                print(f"  ❌ Download failed")
        else:
            print(f"  ❌ Generation failed")

    print(f"\n{'='*60}")
    print("ALL_DONE")


if __name__ == "__main__":
    main()
