#!/usr/bin/env python3
"""
downstream_presence.py — does the XGBoost scene detector actually improve ACTOR
PRESENCE accuracy? Boundary-F1 is only a proxy; this is the number that decides
whether the detector ships.

For each film, compares presence (per-second X-Ray F1) under three regimes:
  A. track_extent            — no flood-fill (claim = [first_seen, last_seen])
  B. flood + histogram cuts  — current shipped flood (snaps to is_cut)
  C. flood + XGBoost bounds  — inject the detector's boundaries into
                               is_scene_boundary (flood prefers it over is_cut)

Injection: write a copy of each dump with frames/is_scene_boundary set from the
XGBoost knee boundaries, then replay --presence-mode flood against that copy.
Uses the shipped model (all-9 fit). Scored with second_score at the 10-knob
optimum config.
"""
from __future__ import annotations
import sys, json, shutil, subprocess, tempfile, os
from pathlib import Path
import numpy as np
import h5py

sys.path.insert(0, "scripts/scene_detector")
sys.path.insert(0, "scripts/optimizer")
sys.path.insert(0, "scripts/validation")
import train_xgb_boundary as XB
from second_score import score_seconds
from sample_eval import load_gallery_keys
import xgboost as xgb

GAL = "experiments/galleries/gallery_LVFace-B_Glint360K.h5"
MODEL = "experiments/results/scene_boundary/xgb_boundary_shipped.json"
# 10-knob presence optimum (shipped config)
CFG = ["--prob-threshold", "0.485", "--ownership-logodds", "1.72",
       "--track-extinction-sec", "31", "--track-alpha", "0.435",
       "--evidence-rho-max", "0.204", "--evidence-admit-below", "0.784",
       "--match-prior", "0.433", "--expand-band-lo", "0.804",
       "--expand-band-hi", "0.952", "--expand-gallery"]


def xgb_boundary_seconds(reg, dump):
    X, yb, ic = XB.per_second_matrix(dump, xr_for(dump), "experiments/dumps/audio_features")
    prob = np.clip(reg.predict(X), 0, 1)
    return set(XB.knee_boundaries(prob))


FILMS = json.load(open("experiments/manifests/films_LVFace_opencv5.json"))
_XR = {f["dump"]: f["xray"] for f in FILMS}
def xr_for(dump): return _XR[dump]


def inject_boundaries(dump, second_set, out_path):
    """Copy dump, set frames/is_scene_boundary=1 at the given integer seconds."""
    shutil.copy(dump, out_path)
    with h5py.File(out_path, "r+") as f:
        ts = f["frames/timestamp_sec"][:]
        bnd = np.zeros(len(ts), np.uint8)
        for i, t in enumerate(ts):
            if int(round(t)) in second_set:
                bnd[i] = 1
        if "frames/is_scene_boundary" in f:
            f["frames/is_scene_boundary"][:] = bnd
        else:
            f["frames"].create_dataset("is_scene_boundary", data=bnd)


def replay(dump, out, mode):
    argv = [".venv-rocm/bin/python" if False else sys.executable,
            "scripts/optimizer/replay.py", "--dump", dump, "--gallery", GAL,
            "--out", out] + CFG
    if mode:
        argv += ["--presence-mode", mode]
    subprocess.run(argv, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=300)
    return json.loads(Path(out).read_text())


def main():
    reg = xgb.XGBRegressor(); reg.load_model(MODEL)
    gk = load_gallery_keys(GAL)
    tmp = tempfile.mkdtemp()
    print(f"{'film':24s} {'trackext':>9} {'flood+hist':>11} {'flood+XGB':>10}")
    agg = {"track_extent": [], "flood_hist": [], "flood_xgb": []}
    for f in FILMS:
        dump, xr = f["dump"], f["xray"]
        out = f"{tmp}/out.json"
        # A. track_extent
        a = score_seconds(replay(dump, out, "track_extent"), xr, gallery_keys=gk)
        # B. flood + histogram cuts (original dump's is_cut; is_scene_boundary=0)
        b = score_seconds(replay(dump, out, "flood"), xr, gallery_keys=gk)
        # C. flood + XGBoost boundaries injected
        inj = f"{tmp}/inj_{f['slug']}.h5"
        inject_boundaries(dump, xgb_boundary_seconds(reg, dump), inj)
        c = score_seconds(replay(inj, out, "flood"), xr, gallery_keys=gk)
        os.unlink(inj)
        agg["track_extent"].append(a["f1"]); agg["flood_hist"].append(b["f1"])
        agg["flood_xgb"].append(c["f1"])
        print(f"{f['name'][:24]:24s} {a['f1']*100:8.1f}% {b['f1']*100:10.1f}% "
              f"{c['f1']*100:9.1f}%")
    print(f"\n{'MACRO-MEAN':24s} {np.mean(agg['track_extent'])*100:8.1f}% "
          f"{np.mean(agg['flood_hist'])*100:10.1f}% {np.mean(agg['flood_xgb'])*100:9.1f}%")
    json.dump({k: float(np.mean(v)) for k, v in agg.items()},
              open("experiments/results/scene_boundary/downstream_presence.json", "w"),
              indent=2)


if __name__ == "__main__":
    main()
