"""
GLAMWalk -- Volume 2: The Gauntlet
10_comfyui_bridge.py

Submitting a workflow to ComfyUI's local API and polling for completion using real wall-clock time (not a loop-count guess), plus per-render timing instrumentation. Requires a running local ComfyUI instance to actually execute.

Source: Volume Two, Sections 4.4 and 4.6
Targets Python 3.11.4 -- see Volume One, Chapter 1.5/1.6.

NOTE: This is a teaching example. Some names (e.g. a placeholder
value or object) stand in for something you'd supply yourself when
adapting the pattern to a real script.
"""

import requests
import time
import uuid

COMFYUI_URL = "http://127.0.0.1:8188"

def queue_prompt(workflow_json):
    """Submit a workflow to ComfyUI and get back a prompt_id to track it."""
    payload = {
        "prompt": workflow_json,
        "client_id": str(uuid.uuid4()),
    }
    response = requests.post(f"{COMFYUI_URL}/prompt", json=payload, timeout=15)
    response.raise_for_status()
    return response.json()["prompt_id"]

def wait_for_completion(prompt_id, timeout_seconds=1200, poll_seconds=8):
    """
    Poll ComfyUI's history endpoint until this specific job appears
    as complete, using REAL elapsed wall-clock time for the timeout —
    not a naive loop-iteration counter (see 4.5 for why that distinction
    is the whole point of this function).
    """
    start_time = time.time()

    while True:
        elapsed = time.time() - start_time
        if elapsed > timeout_seconds:
            # A job stuck this long server-side should be actively
            # cancelled, not just abandoned client-side — an abandoned
            # job can keep running and eat the NEXT job's time budget too.
            requests.post(f"{COMFYUI_URL}/interrupt", timeout=10)
            raise TimeoutError(f"Render timed out after {elapsed:.0f}s")

        history = requests.get(f"{COMFYUI_URL}/history/{prompt_id}", timeout=poll_seconds).json()
        if prompt_id in history:
            return history[prompt_id]   # job complete — full result data

        time.sleep(poll_seconds)


def render_with_timing(workflow_json):
    """Break a render's total time into its real phases, using ComfyUI's
    OWN server-side timing where available — not a client-side guess."""
    t_submit = time.time()
    prompt_id = queue_prompt(workflow_json)
    t_queued = time.time()

    result = wait_for_completion(prompt_id)
    t_done = time.time()

    # ComfyUI's history entry includes its own server-side execution
    # timing and a flag for whether loader nodes stayed resident
    # between jobs (i.e., whether a multi-GB model had to be
    # re-dequantized from scratch for THIS job specifically).
    status = result.get("status", {})
    was_cached = "execution_cached" in str(status)

    return {
        "queue_wait_seconds": t_queued - t_submit,
        "total_seconds": t_done - t_submit,
        "models_stayed_resident": was_cached,
    }
