#!/usr/bin/env python3
"""
rematch_frames.py — remake each named July frame example against the CURRENT
pipeline. For a file named <film>_<...>_<actor>.jpg, find a second in this film's
replay where that actor is drawn in the matching class (FP for *_fpi_*, TP for
*_tp/perfect*), extract + annotate it, and write it over the doc asset. Reports
which July examples no longer reproduce (honest — the config/model changed).

Needs the per-film raw replay (experiments/dumps + replay --raw-out already run by
regen_frame_examples.sh into the scratch predictions). Reads those.
"""
from __future__ import annotations
import json, sys, subprocess, re
from pathlib import Path
sys.path.insert(0, "scripts/optimizer"); sys.path.insert(0, "scripts/validation")
import dump_error_frames as D
from second_score import load_second_timeline, _match

SP = Path("/tmp/claude-1000/-home-dtourolle-Development-scene-actor-extraction/"
          "c579f8cf-2974-4cbd-be88-afec68dbbf58/scratchpad")
ASSETS = Path("docs/assets/images")
LUT = json.load(open("experiments/file-lut.json"))
FILMS = json.load(open("experiments/manifests/films_LVFace_opencv5.json"))
XR = {f["slug"]: f["xray"] for f in FILMS}

# filename → (film slug, actor substring, class). class: "fp" | "tp".
# actor substring is matched case-insensitively against drawn names.
JOBS = {
    "lord_of_war_fpi_reddick.jpg":       ("Lord_of_War", "reddick", "fp"),
    "lord_of_war_fpi_shumbris.jpg":      ("Lord_of_War", "shumbris", "fp"),
    "lord_of_war_fpi_reagan_photo.jpg":  ("Lord_of_War", "reagan", "fp"),
    "lovelace_fpi_sevigny.jpg":          ("Lovelace", "sevigny", "fp"),
    "lovelace_robert_patrick_fpi.jpg":   ("Lovelace", "patrick", "fp"),
    "lovelace_perfect_second.jpg":       ("Lovelace", None, "tp"),
    "lovelace_polygraph_bridged.jpg":    ("Lovelace", None, "tp"),
    "many_saints_fpi_deschanel.jpg":     ("The_Many_Saints_of_Newark", "deschanel", "fp"),
    "many_saints_fpi_gardner.jpg":       ("The_Many_Saints_of_Newark", "gardner", "fp"),
    "many_saints_fpi_yates.jpg":         ("The_Many_Saints_of_Newark", "yates", "fp"),
    "many_saints_outofcast_fpi.jpg":     ("The_Many_Saints_of_Newark", None, "fp"),
    "scarface_fpi_alley.jpg":            ("Scarface", "alley", "fp"),
    "downton_crew_fn.jpg":               ("Downton_Abbey__A_New_Era", None, "tp"),
    "downton_wedding_couple.jpg":        ("Downton_Abbey__A_New_Era", None, "tp"),
    "downton_tp_example.jpg":            ("Downton_Abbey__A_New_Era", None, "tp"),
    "valerian_screen_call.jpg":          ("Valerian_and_the_City_of_a_Thousand_Plan", None, "tp"),
    "cafe_society_rapid_cut.jpg":        ("Café_Society", None, "tp"),
    # germar_beats_xray / downton_funeral_19of20 are July-narrative-specific; skip.
}


def gt_keysets(slug):
    tl, _, _ = load_second_timeline(XR[slug])
    return tl


def main():
    made, missing = [], []
    for fname, (slug, actor, cls) in JOBS.items():
        raw = SP / f"{slug}_raw.jsonl"
        if not raw.exists():
            missing.append((fname, "no raw replay")); continue
        tl = gt_keysets(slug)
        best = None  # (t, actor_dict, fp_keys)
        for line in open(raw):
            d = json.loads(line)
            t = int(d["timestamp_sec"])
            drawn = [a for a in d.get("visible_actors", []) if a.get("actor_idx", -1) >= 0]
            if not drawn:
                continue
            gt = tl.get(t, [])
            fp_keys = {D._name_key(a["name"]) for a in drawn
                       if not any(D._name_key(a["name"]) in g for g in gt)}
            for a in drawn:
                nk = D._name_key(a["name"]); is_fp = nk in fp_keys
                if actor and actor not in a["name"].lower():
                    continue
                match = (is_fp if cls == "fp" else not is_fp)
                if not match:
                    continue
                # prefer high similarity + a clean single-subject frame
                score = a["similarity"] - 0.05*len(drawn)
                if best is None or score > best[3]:
                    best = (t, d, fp_keys, score)
        if best is None:
            missing.append((fname, f"no current {cls} for {actor or 'any'}")); continue
        t, d, fp_keys, _ = best
        # FN names at t: X-Ray scene cast whose keyset matches no drawn face.
        gt = tl.get(t, [])
        drawn_keys = [set(D._name_key(a["name"]).replace("name:", "") for _ in [0])
                      for a in d.get("visible_actors", []) if a.get("actor_idx", -1) >= 0]
        drawn_ks = [D._name_key(a["name"]) for a in d.get("visible_actors", [])
                    if a.get("actor_idx", -1) >= 0]
        fn_names = []
        for ga in gt:
            if not any(dk in ga for dk in drawn_ks):
                readable = sorted(x for x in ga
                                  if not x.startswith("imdb:") and not x.startswith("tmdb:")
                                  and not x.startswith("jf:"))
                if readable:
                    fn_names.append(readable[0])
        out = ASSETS / fname
        try:
            D.extract_frame(LUT[slug], t, out)
            D.draw_annotations(out, d["visible_actors"], fp_keys=fp_keys,
                               fn_names=fn_names)
            made.append((fname, slug, t))
        except subprocess.CalledProcessError:
            missing.append((fname, "ffmpeg failed"))

    print("=== remade ===")
    for f, s, t in made: print(f"  {f}  ({s} t={t}s)")
    print("=== no current equivalent (left as-is / flag in doc) ===")
    for f, why in missing: print(f"  {f}  — {why}")


if __name__ == "__main__":
    main()
