#!/usr/bin/env python3
"""Compose AI4M 'Mi IA dijo que mis proyectos son SUYOS' (~74s) con el pipeline actual:
3 segmentos -> avatar_b1 (hook+setup) + hero_waveform (giro, audio real) + avatar_b2b3 (angulo+CTA).
Sistema de captions del SPEC (serif dinamico + mix + safe-zone Shorts) por segmento avatar, push-in en el hook,
b-rolls reales (tweet/telegram) + card. Luego concat."""
import subprocess, pathlib, sys, re
sys.path.insert(0, "/home/clawd/bin/agentsquad-shorts")
from lib import captions

BASE = "/home/clawd/playgrounds/ai4m-ia-mia"
WORK = pathlib.Path("/tmp/ia-mia"); WORK.mkdir(exist_ok=True)
OUTF = f"{BASE}/final_mia.mp4"

CAP_Y = 1075
CHARF = 0.362; SERIF_TARGET_W = 760; SERIF_MAXFS = 236; SERIF_RATIO = 0.72
STYLES = {"Box": (94, 17, 132), "Serif": (200, 10, 144)}

def _ts(t):
    h=int(t//3600); m=int((t%3600)//60); s=t%60; return f"{h:d}:{m:02d}:{s:05.2f}"
def _wrap(text, maxc, cap=3):
    words=text.split(); lines=[]; cur=""
    for w in words:
        if cur and len(cur)+1+len(w)>maxc: lines.append(cur); cur=w
        else: cur=(cur+" "+w).strip()
    if cur: lines.append(cur)
    return lines[:cap]
def _wrap_serif(text):
    words=text.split()
    if not words: return [text]
    mw=max(len(w) for w in words)
    for cand in range(mw, len(text)+2):
        l=_wrap(text, cand, cap=99)
        if len(l)<=3: return l
    return _wrap(text, len(text)+2, cap=99)[:3]

def fix_brand(ws):
    out=[]; i=0
    while i < len(ws):
        w=ws[i]; wl=str(w.get("word","")).strip().lower().strip(".,!?")
        if wl=="ai" and i+2<len(ws):
            w1=str(ws[i+1].get("word","")).strip().lower().strip(".,!?")
            w2raw=str(ws[i+2].get("word","")).strip(); w2=w2raw.lower().strip(".,!?")
            if w1 in ("for","form","four","fore") and w2.startswith("manager"):
                trail="." if w2raw.endswith(".") else ""
                out.append({"word":"AI4Managers"+trail,"start":float(w.get("start",0)),"end":float(ws[i+2].get("end",0))}); i+=3; continue
        out.append(w); i+=1
    return out

def build_ass(words, out_path, sup, hook_end=0.0):
    head=("[Script Info]\nScriptType: v4.00+\nPlayResX: 1080\nPlayResY: 1920\nScaledBorderAndShadow: yes\nWrapStyle: 2\n\n"
          "[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n"
          "Style: Box,DejaVu Sans,94,&H00141414,&H00141414,&H00FFFFFF,&H00FFFFFF,1,0,0,0,100,100,1,0,3,9,0,5,60,60,0,1\n"
          "Style: Serif,PP Editorial New,200,&H00FFFFFF,&H00FFFFFF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,0,2,5,60,60,0,1\n\n"
          "[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")
    def sub_sup(t0,t1):
        segs=[(t0,t1)]
        for ws,we in sup:
            out=[]
            for a,b in segs:
                if we<=a or ws>=b: out.append((a,b)); continue
                if a<ws: out.append((a,ws))
                if we<b: out.append((we,b))
            segs=out
        return [(a,b) for a,b in segs if b-a>0.30]
    chunks=captions._phrase_chunks(words)
    starts=[float(c[0]["start"]) for c in chunks]
    ev=[]
    for i,ch in enumerate(chunks):
        txt=" ".join(str(w["word"]).strip() for w in ch).strip(); txt=re.sub(r"\s+"," ",txt)
        if not txt: continue
        t0=float(ch[0]["start"]); t1=float(ch[-1].get("end",t0+0.4))+0.12
        if i+1<len(starts): t1=min(t1, starts[i+1]-0.02)
        if t1<=t0: t1=t0+0.20
        style="Box" if (i%2==0 or txt.rstrip().endswith("?")) else "Serif"
        if t0<hook_end: style="Serif"
        fs,maxc,lh=STYLES[style]
        if style=="Serif":
            wl=_wrap_serif(txt); longest=max(len(l) for l in wl) if wl else 1
            fs=min(SERIF_MAXFS, int(SERIF_TARGET_W/(longest*CHARF))); lh=int(round(SERIF_RATIO*fs))
        else:
            wl=_wrap(txt, maxc)
        N=len(wl)
        segs=sub_sup(t0,t1)
        if not segs: continue
        for k,line in enumerate(wl):
            y=int(CAP_Y+(k-(N-1)/2.0)*lh)
            eff=("\\fs%d\\pos(540,%d)"%(fs,y)) if style=="Serif" else ("\\pos(540,%d)"%y)
            for a,b in segs:
                ev.append(f"Dialogue: 0,{_ts(a)},{_ts(b)},{style},,0,0,0,,{{{eff}}}{line}")
    open(out_path,"w").write(head+"\n".join(ev)+"\n")

def run(cmd):
    r=subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode!=0: raise RuntimeError("FAIL: "+" ".join(map(str,cmd))+"\n"+r.stderr[-1800:])

def process(idx, av, script, brolls, hook=False):
    """av con captions + push-in(hook) + b-rolls -> seg{idx}.mp4 (con audio)."""
    print(f"  seg{idx}: captions + {len(brolls)} b-rolls" + (" + push-in" if hook else ""))
    w=captions.transcribe_words(av)
    w=captions.align_to_script(w, open(script).read().strip())
    w=fix_brand(w)
    sup=[(s-0.10, e+0.35) for _,s,e in brolls]
    ass=WORK/f"s{idx}.ass"
    build_ass(w, str(ass), sup, hook_end=6.2 if hook else 0.0)
    inputs=["-i",av]
    for clip,_,_ in brolls: inputs+=["-i",clip]
    pushin="" if not hook else ",zoompan=z='if(lte(on,240),1+0.0003*on,1.072)':d=1:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1080x1920:fps=30"
    parts=[f"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,fps=30{pushin},setsar=1,subtitles={ass}[base]"]
    base="[base]"
    for j,(clip,s,e) in enumerate(brolls):
        dur=e-s
        parts.append(f"[{j+1}:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,fps=30,setsar=1,fade=t=out:st={dur-0.12:.2f}:d=0.12:alpha=1,setpts=PTS+{s:.2f}/TB[ov{j}]")
    for j,(clip,s,e) in enumerate(brolls):
        lbl="[v]" if j==len(brolls)-1 else f"[c{j}]"
        parts.append(f"{base}[ov{j}]overlay=0:0:enable='between(t,{s:.2f},{e:.2f})'{lbl}"); base=lbl
    if not brolls: parts[-1]=parts[-1].replace("[base]","[v]")
    fc=";".join(parts)
    out=str(WORK/f"seg{idx}.mp4")
    run(["ffmpeg","-y",*inputs,"-filter_complex",fc,"-map","[v]","-map","0:a",
         "-af","loudnorm=I=-16:TP=-1.5","-c:v","libx264","-preset","veryfast","-crf","19","-pix_fmt","yuv420p","-c:a","aac","-b:a","192k","-r","30","-ar","44100",out])
    return out

print("seg1 (avatar B1 hook+setup)...")
s1=process(1, f"{BASE}/avatar_b1.mp4", f"{BASE}/script_b1.txt",
           [(f"{BASE}/brolls/bt_tweet.mp4",8.8,12.8),(f"{BASE}/brolls/bt_telegram.mp4",16.3,20.8)], hook=True)
print("seg2 (hero waveform — normalizar)...")
s2=str(WORK/"seg2.mp4")
run(["ffmpeg","-y","-i",f"{BASE}/hero_waveform.mp4","-vf","scale=1080:1920,fps=30,setsar=1","-af","loudnorm=I=-16:TP=-1.5","-c:v","libx264","-preset","veryfast","-crf","19","-pix_fmt","yuv420p","-c:a","aac","-b:a","192k","-r","30","-ar","44100",s2])
print("seg3 (avatar B2+B3 angulo+CTA)...")
s3=process(3, f"{BASE}/avatar_b2b3.mp4", f"{BASE}/script_b2b3.txt",
           [(f"{BASE}/brolls/bt_card.mp4",22.3,25.3)], hook=False)

print("concat...")
cl=WORK/"concat.txt"; cl.write_text("".join(f"file '{p}'\n" for p in [s1,s2,s3]))
run(["ffmpeg","-y","-f","concat","-safe","0","-i",str(cl),"-c","copy","-movflags","+faststart",OUTF])
dur=subprocess.check_output(["ffprobe","-v","error","-show_entries","format=duration","-of","csv=p=0",OUTF]).decode().strip()
print(f"OK -> {OUTF} ({float(dur):.1f}s)")
