#!/usr/bin/env python3
"""JUDGE do-zero dos missed-candidates (Kimi via vlm.py, prompt versionado). Pra cada candidato:
frame da cam CANDIDATA no meio do span + frame da cam ATIVA no mesmo instante -> VLM decide se o
insert vale. READ-ONLY nos originais (ffmpeg só -i; saída no scratch). -> missed_inserts.json
Uso: missed_judge.py [--limit N]"""
import json, os, sys, bisect, re
from vlm import ask_images, par_map
import cache_manifest as cm
import judgelib as J

_PROMPT_PATH = os.path.join(os.path.dirname(__file__), "prompts/missed_judge_v1.md")
PROMPT = open(_PROMPT_PATH).read()
OUT = "missed_inserts.json"

cps = sorted(json.load(open("versions/UNI.json"))["cutpoints"], key=lambda c: c["frame"])
cf = [c["frame"] for c in cps]
def active_at(f): return cps[max(0, bisect.bisect_right(cf, f)-1)]["angle"]

def main():
    limit = int(sys.argv[sys.argv.index("--limit")+1]) if "--limit" in sys.argv else 10**9
    # CACHE: content-hash dos inputs; judgelib/vlib no mtime (frame_of).
    _sig = {"content": cm.content_signature(["missed_candidates.json", "versions/UNI.json"]),
            "mtime": cm.input_signature(["placements.json", _PROMPT_PATH,
                                          os.path.join(os.path.dirname(__file__), "judgelib.py"),
                                          os.path.join(os.path.dirname(__file__), "vlib.py")]),
            "limit": limit}
    if cm.is_fresh(".", "missed_judge", __file__, _sig) and os.path.exists(OUT):
        print(f"[cache] missed_judge: script+input inalterados, pulando VLM — reusando {OUT}")
        return
    cands = json.load(open("missed_candidates.json"))[:limit]
    def work(ic):
        i, c = ic
        mid = (c["f0"] + c["f1"]) // 2
        a = J.frame_of(c["cam"], mid)
        b = J.frame_of(active_at(mid), mid)
        if not a or not b:
            print(f"  {c['tc']} {c['type']}: sem frame, pula"); return None
        txt, _ = ask_images([a, b], PROMPT.replace("{TYPE}", c["type"]))
        m = re.search(r'\{.*\}', txt, re.S)
        try: v = json.loads(m.group(0)) if m else {}
        except Exception: v = {}
        keep = bool(v.get("keep"))
        print(f"  [{i+1}/{len(cands)}] {c['tc']} cam{c['cam']} {c['type']:16} -> {'KEEP' if keep else 'drop'} ({str(v.get('why',''))[:60]})")
        if not keep: return None
        return {"f0": c["f0"], "f1": c["f1"], "angle": c["cam"], "type": c["type"],
                "tc": c["tc"], "why": v.get("why", "")}
    out = [r for r in par_map(work, list(enumerate(cands))) if r]
    kept = len(out)
    json.dump(out, open(OUT, "w"), indent=1)
    cm.mark_done(".", "missed_judge", __file__, _sig)
    print(f"\nmissed_inserts.json: {kept}/{len(cands)} confirmados pelo judge")

if __name__ == "__main__":
    main()
