#!/usr/bin/env python3
"""Apply validated boundaries → re-split Take89 into per-track audio for listening.

Reads boundaries_take89_validated.json, sums the 12 Take89 orbit-stems per track
window (decode-accurate -ss seek, so only the window is read), loudnorm -14 for
comfortable audition, fades, FLAC + metadata tags (provenance-checked), and writes
a manifest. Output → freebox Prod/Montreuil26_resplit/ for PLN to listen.

NOTE: review-quality per-track audio (a normalized stem-sum), NOT final masters.
Final per-track mastering = tidal-ears master, later. Gig metadata pulled from the
canonical site lives content (see boundaries JSON `gig`), not invented.
"""
import json, subprocess, sys
from pathlib import Path

HERE = Path(__file__).parent
STEMS = Path("/mnt/freebox/PLN/Work/Sound/Prod/_stems/2026-05-22_Take89")
OUT = Path("/mnt/freebox/PLN/Work/Sound/Prod/Montreuil26_resplit")
B = json.loads((HERE / "boundaries_take89_validated.json").read_text())

# 15 tracks: track1 = opener, tracks 2..15 = each boundary's "into"
TRACK1 = "Quand on décolle"
bounds = B["boundaries"]
titles = [TRACK1] + [b["into"] for b in bounds]
starts = [0.0] + [b["t"] for b in bounds]
SET_END = 4690.0  # trailing; Revolution fades out (encore/claps trim = later edit)
ends = starts[1:] + [SET_END]

gig = B["gig"]
ALBUM = gig["set_title"]

def sanitize(s):
    return "".join(c if c.isalnum() or c in " -_'" else "_" for c in s).strip()

def run(args):
    r = subprocess.run(args, capture_output=True, text=True)
    if r.returncode != 0:
        print(r.stderr[-1500:], file=sys.stderr)
    return r

def main():
    OUT.mkdir(parents=True, exist_ok=True)
    manifest = {"take": B["take"], "date": B["date"], "gig": gig,
                "time_base": B["time_base"], "quality": "review (normalized stem-sum, -14 LUFS); not a final master",
                "tracks": []}
    for i, (title, s, e) in enumerate(zip(titles, starts, ends), 1):
        dur = round(e - s, 2)
        fn = f"{i:02d}-{sanitize(title)}.flac"
        out = OUT / fn
        # 12 orbit inputs, each seeked to s
        inputs = []
        for o in range(1, 13):
            inputs += ["-accurate_seek", "-ss", str(s), "-t", str(dur),
                       "-i", str(STEMS / f"orbit-{o:02d}.flac")]
        fo = max(0.0, dur - 3.0)
        graph = (f"amix=inputs=12:normalize=0,volume=-9dB,"
                 f"afade=t=in:st=0:d=0.3,afade=t=out:st={fo}:d=3,"
                 f"loudnorm=I=-14:TP=-1:LRA=11")
        tags = ["-metadata", f"title={title}", "-metadata", "artist=ParVagues",
                "-metadata", f"album={ALBUM}", "-metadata", f"date={B['date']}",
                "-metadata", f"track={i}/15",
                "-metadata", f"comment=live @ {gig['venue']} | take {B['take']} | "
                             f"window {s}-{round(e,1)}s | src {gig['src']}"]
        print(f"  [{i:02d}/15] {title:<28} [{int(s//60)}:{int(s%60):02d}–{int(e//60)}:{int(e%60):02d}] {dur}s", flush=True)
        r = run(["ffmpeg", "-hide_banner", "-loglevel", "error", *inputs,
                 "-filter_complex", graph, *tags,
                 "-ar", "44100", "-c:a", "flac", "-compression_level", "6",
                 str(out), "-y"])
        ok = out.exists() and r.returncode == 0
        manifest["tracks"].append({"n": i, "title": title, "file": fn,
                                   "start_s": round(s, 1), "end_s": round(e, 1),
                                   "dur_s": dur, "ok": ok})
    (OUT / "manifest.json").write_text(json.dumps(manifest, indent=2))
    print(f"\n✓ {sum(t['ok'] for t in manifest['tracks'])}/15 tracks → {OUT}")
    print(f"  manifest: {OUT}/manifest.json")

if __name__ == "__main__":
    main()
