#!/usr/bin/env python3
import json, math, re, subprocess, time
from pathlib import Path
PROJECT=Path('/data/video-pipeline/Channel-Loi-Co-Nhan/project/11062026-001-Biet-Du-Thi-Khong-Nhuc')
LOGO=Path('/data/video-pipeline/Channel-Loi-Co-Nhan/data/logo/logoloiconhan.png')
BGM=Path('/data/video-pipeline/Channel-Loi-Co-Nhan/data/bgm/paulyudin-sentimental-story-182512.mp3')
LOG=PROJECT/'logs/fix_sub_audio_v2.log'
SUB_FONT_SIZE=10
SUB_MARGIN_V=55
MAX_CHARS=34

def log(s):
    line=time.strftime('%F %T ')+s
    print(line, flush=True)
    LOG.parent.mkdir(parents=True, exist_ok=True)
    with LOG.open('a', encoding='utf-8') as f: f.write(line+'\n')

def run(cmd):
    log('RUN '+' '.join(map(str, cmd)))
    subprocess.check_call(list(map(str, cmd)))

def probe(p):
    return json.loads(subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration','-show_streams','-of','json',str(p)], text=True))

def dur(p):
    return float(probe(p)['format']['duration'])

def ts(sec):
    ms=int(round(sec*1000)); h=ms//3600000; ms%=3600000; m=ms//60000; ms%=60000; s=ms//1000; ms%=1000
    return f'{h:02}:{m:02}:{s:02},{ms:03}'

def chunks(text, max_chars=MAX_CHARS):
    text=' '.join(text.replace('\n',' ').split())
    parts=[]
    # Prefer natural comma/period chunks first.
    for piece in re.split(r'(?<=[,.;:!?…])\s+', text):
        words=piece.split()
        cur=[]
        for w in words:
            cand=' '.join(cur+[w])
            if cur and len(cand)>max_chars:
                parts.append(' '.join(cur))
                cur=[w]
            else:
                cur.append(w)
        if cur:
            parts.append(' '.join(cur))
    return [p for p in parts if p]

def write_srt():
    timeline=json.loads((PROJECT/'audio/master_narration_timeline.json').read_text(encoding='utf-8'))['items']
    scenes={s['scene_id']:s for s in json.loads((PROJECT/'script/scenes.json').read_text(encoding='utf-8'))['scenes']}
    blocks=[]; idx=1
    for it in timeline:
        text=scenes[it['scene_id']]['narration']
        cs=chunks(text)
        start=float(it['start']); end=float(it['speech_end']); total=max(0.1,end-start)
        weights=[max(1,len(c)) for c in cs]
        wsum=sum(weights)
        t=start
        for c,w in zip(cs,weights):
            d=total*w/wsum
            e=min(end,t+d)
            blocks.append(f"{idx}\n{ts(t)} --> {ts(e)}\n{c}\n")
            idx+=1; t=e
    sub=PROJECT/'subtitles/master_subtitles_one_line_bottom_split.srt'
    sub.parent.mkdir(parents=True, exist_ok=True)
    sub.write_text('\n'.join(blocks), encoding='utf-8')
    return sub

def main():
    LOG.write_text('', encoding='utf-8')
    sub=write_srt()
    seglist=PROJECT/'render_onepass/segments.txt'
    master=PROJECT/'audio/master_narration.wav'
    mainv=PROJECT/'output/main_story.mp4'
    fc=(
        f"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,"
        f"subtitles='{sub}':force_style='FontName=DejaVu Sans,FontSize={SUB_FONT_SIZE},Outline=1,Alignment=2,MarginV={SUB_MARGIN_V},WrapStyle=2'[base];"
        f"movie='{LOGO}',scale=160:-1,format=rgba,colorchannelmixer=aa=0.55[wm];"
        f"[base][wm]overlay=W-w-35:35[v]"
    )
    run(['ffmpeg','-y','-v','error','-f','concat','-safe','0','-i',seglist,'-i',master,'-filter_complex',fc,'-map','[v]','-map','1:a','-r','30','-c:v','libx264','-pix_fmt','yuv420p','-c:a','aac','-b:a','192k','-ar','44100','-ac','2','-shortest',mainv])
    intro=PROJECT/'output/intro/intro.mp4'
    pause=PROJECT/'output/intro/intro_pause_4s.mp4'
    final=PROJECT/'output/final/final_video.mp4'
    # Build final in one ffmpeg graph so voice cannot disappear from concat-copy edge cases.
    filter_complex=(
        '[0:v]setpts=PTS-STARTPTS[v0];[0:a]aresample=44100:async=1:first_pts=0[a0];'
        '[1:v]setpts=PTS-STARTPTS[v1];[1:a]aresample=44100:async=1:first_pts=0[a1];'
        '[2:v]setpts=PTS-STARTPTS[v2];[2:a]aresample=44100:async=1:first_pts=0[a2];'
        '[v0][a0][v1][a1][v2][a2]concat=n=3:v=1:a=1[vcat][voice];'
        '[3:a]aresample=44100,volume=0.10[bgm];'
        '[voice]volume=1.8[voiceboost];'
        '[voiceboost][bgm]amix=inputs=2:duration=first:dropout_transition=2,alimiter=limit=0.95[a]'
    )
    run(['ffmpeg','-y','-v','error','-i',intro,'-i',pause,'-i',mainv,'-stream_loop','-1','-i',BGM,'-filter_complex',filter_complex,'-map','[vcat]','-map','[a]','-r','30','-c:v','libx264','-pix_fmt','yuv420p','-c:a','aac','-b:a','192k','-ar','44100','-ac','2','-shortest',final])
    run(['ffmpeg','-v','error','-i',final,'-f','null','-'])
    info=json.loads((PROJECT/'output/info.json').read_text(encoding='utf-8'))
    info.update({'duration_sec':dur(final),'subtitle_mode':'one_line_bottom_split','subtitle_font_size':SUB_FONT_SIZE,'subtitle_max_chars':MAX_CHARS,'audio_fix':'concat_filter_voice_boost_bgm_low'})
    (PROJECT/'output/info.json').write_text(json.dumps(info, ensure_ascii=False, indent=2), encoding='utf-8')
    log('COMPLETE '+str(final))
if __name__=='__main__': main()
