#!/usr/bin/env python3
import json, time, urllib.request, subprocess
from pathlib import Path

PROJECT=Path('/data/video-pipeline/Youtube-Video-Maker/Project/30052026-001-Mac-Dinh-Chi')
PROBE='http://192.168.1.104:8018/probe'
BLACK='http://192.168.1.104:8018/blackdetect'
FINAL=PROJECT/'output/final/final_video.mp4'
FINAL_PROBE=PROJECT/'output/final/final_video_probe.json'
PKG_REPORT=PROJECT/'output/final/final_package_report.json'
QA_REPORT=PROJECT/'output/final/final_qa_report.json'
INFO=PROJECT/'output/info.json'
THUMB16=PROJECT/'output/thumbnails/thumb_16x9_final.png'
THUMB9=PROJECT/'output/thumbnails/thumb_9x16_final.png'
MANIFEST=PROJECT/'logs/step19_final_qa_info_manifest.json'

def post(url,payload,timeout=21600):
    req=urllib.request.Request(url,data=json.dumps(payload,ensure_ascii=False).encode(),headers={'Content-Type':'application/json'},method='POST')
    with urllib.request.urlopen(req,timeout=timeout) as r: return json.loads(r.read().decode())
def load(p, default=None):
    return json.loads(p.read_text(encoding='utf-8')) if p.exists() else default
def dur(pr): return float(pr.get('format',{}).get('duration') or 0)
def has(pr,t): return any(s.get('codec_type')==t for s in pr.get('streams',[]))
def count_files(path, glob): return len(list(Path(path).glob(glob))) if Path(path).exists() else 0

def extract_frames(times):
    outdir=PROJECT/'output/final/final_qa_frames'; outdir.mkdir(parents=True,exist_ok=True)
    frames=[]
    for label,sec in times:
        out=outdir/f'{label}_{sec:.1f}.jpg'
        cmd=['sshpass','-p','11210119','ssh','-o','StrictHostKeyChecking=no','-o','PreferredAuthentications=password','-o','PubkeyAuthentication=no','root@192.168.1.104',
             f"ffmpeg -y -hide_banner -loglevel error -ss {sec:.3f} -i '{FINAL}' -frames:v 1 -q:v 2 '{out}'"]
        subprocess.run(cmd,check=True,timeout=120)
        frames.append({'label':label,'time_sec':sec,'path':str(out),'exists':out.exists()})
    return frames

def parse_meta():
    txt=(PROJECT/'script/source_story.txt').read_text(encoding='utf-8')
    series='Việt Sử Kể Lại'; title='NGƯỜI CON GÁI HÁI DÂU BƯỚC VÀO LONG MẠCH ĐẠI VIỆT'
    for line in txt.splitlines():
        low=line.lower()
        if low.startswith('series:'): series=line.split(':',1)[1].strip()
        if low.startswith('tiêu đề truyện:') or low.startswith('tieu de truyen:'): title=line.split(':',1)[1].strip().title()
    return series,title

def main():
    if not FINAL.exists(): raise SystemExit('missing final')
    pr=post(PROBE,{'input_path':str(FINAL)},120)
    FINAL_PROBE.write_text(json.dumps(pr,ensure_ascii=False,indent=2),encoding='utf-8')
    pkg=load(PKG_REPORT,{})
    black=post(BLACK,{'input_path':str(FINAL),'log_path':str(PROJECT/'output/final/final_qa_blackdetect.log')},21600)
    total=dur(pr)
    intro_dur=dur(pkg.get('input_probes',{}).get('intro',{})) or 16.2
    main_dur=dur(pkg.get('input_probes',{}).get('main',{})) or 697.682
    outro_start=max(0,total-25)
    times=[('intro_transition',max(0,intro_dur-1)),('early_main',intro_dur+30),('mid_main',intro_dur+main_dur/2),('late_main',intro_dur+max(0,main_dur-45)),('outro',outro_start+5)]
    frames=extract_frames(times)
    master_probe=load(PROJECT/'audio/master_audio_probe.json',{})
    main_probe=load(PROJECT/'output/main_story_probe.json',{})
    scenes=load(PROJECT/'script/scenes.json',{}).get('scenes',[])
    parts=load(PROJECT/'parts/part_ranges.json',{}).get('parts',[])
    series,title=parse_meta()
    validation={
        'final_exists':FINAL.exists() and FINAL.stat().st_size>1000,
        'has_audio':has(pr,'audio'),'has_video':has(pr,'video'),
        'thumbnail_16x9_exists':THUMB16.exists(),'thumbnail_9x16_exists':THUMB9.exists(),
        'black_event_count':black.get('event_count',0),'blackdetect_clear':black.get('event_count',0)==0,
        'qa_frames_extracted':all(f['exists'] for f in frames),
        'package_completed':pkg.get('status')=='completed',
        'bgm_volume':pkg.get('validation',{}).get('bgm_volume'),
        'decision':'approved_output_candidate_after_automated_qa; human spot-check recommended for subjective BGM/sync'
    }
    passed=all([validation['final_exists'],validation['has_audio'],validation['has_video'],validation['thumbnail_16x9_exists'],validation['thumbnail_9x16_exists'],validation['blackdetect_clear'],validation['qa_frames_extracted'],validation['package_completed']])
    qa={'project_id':PROJECT.name,'step':19,'passed':passed,'status':'passed' if passed else 'failed','final_video_path':str(FINAL),'final_duration_sec':total,'qa_points':times,'qa_frames':frames,'blackdetect':black,'validation':validation,'notes':['Automated QA checks streams, duration, black frames, frames at intro/early/mid/late/outro. User should still listen/spot-check BGM and sync before publishing.']}
    QA_REPORT.write_text(json.dumps(qa,ensure_ascii=False,indent=2),encoding='utf-8')
    yt_tags=['#vetmucthoigian','#ylan','#nguyenphiylan','#nhaly','#daiviet','#vietsukelai','#lichsuvietnam']
    info={
        'project_id':PROJECT.name,'series':series,'title':title,'approved_output':passed,'qa_final_status':qa['status'],
        'paths':{
            'source_script':str(PROJECT/'script/source_story.txt'),'cleaned_story':str(PROJECT/'script/cleaned_story.txt'),'dense_plan':str(PROJECT/'script/scenes.json'),'character_bible':str(PROJECT/'script/character_bible.json'),
            'scene_images_dir':str(PROJECT/'images/scenes'),'thumbnails':{'16x9':str(THUMB16),'9x16':str(THUMB9)},'voice_segments_dir':str(PROJECT/'audio/voice_segments'),'scene_tts_dir':str(PROJECT/'audio/scenes'),'master_narration':str(PROJECT/'audio/master_narration.wav'),
            'subtitles':str(PROJECT/'subtitles/master_subtitles_clamped.srt'),'part_ranges':str(PROJECT/'parts/part_ranges.json'),'rendered_parts_dir':str(PROJECT/'render_parts'),'main_story':str(PROJECT/'output/main_story.mp4'),
            'intro':str(PROJECT/'output/intro/intro.mp4'),'final_output':str(FINAL),'final_qa_report':str(QA_REPORT),'final_package_report':str(PKG_REPORT)
        },
        'durations':{'master_audio_sec':master_probe.get('master_duration_sec') or dur(load(PROJECT/'audio/master_audio_probe.json',{})),'main_story_sec':dur(main_probe),'final_sec':total},
        'counts':{'scenes':len(scenes),'scene_images':count_files(PROJECT/'images/scenes','scene_*.png'),'parts':len(parts)},
        'social_metadata':{
            'title_youtube_1':'Ỷ Lan: Người Con Gái Hái Dâu Bước Vào Long Mạch Đại Việt | Việt Sử Kể Lại',
            'title_youtube_2':'Nguyên Phi Ỷ Lan - Từ Gốc Lan Thôn Quê Đến Quyền Lực Nhà Lý',
            'title_youtube_3':'Ỷ Lan: Ánh Sáng Và Bóng Tối Của Linh Nhân Hoàng Thái Hậu',
            'description_youtube':'Cùng Vết Mực Thời Gian kể lại cuộc đời Nguyên phi Ỷ Lan: từ cô gái hái dâu bên gốc lan đến Hoàng thái hậu, nhiếp chính, người góp phần giữ ổn định triều Lý và để lại di sản phức tạp trong lịch sử Đại Việt.',
            'hashtag_youtube':' '.join(yt_tags),
            'title_facebook':'Ỷ Lan - người phụ nữ bước từ gốc lan vào lịch sử Đại Việt',
            'description_facebook':'Một hành trình về Nguyên phi Ỷ Lan, người phụ nữ quyền lực thời Lý với công lớn, vết tối và dấu ấn sâu đậm trong dân gian.',
            'hashtag_facebook':' '.join(yt_tags),
            'title_tiktok':'Ỷ Lan - từ gốc lan đến ngai vàng nhà Lý',
            'description_tiktok':'Câu chuyện về Nguyên phi Ỷ Lan, người phụ nữ đặc biệt của lịch sử Đại Việt.',
            'hashtag_tiktok':' '.join(yt_tags)
        }
    }
    INFO.write_text(json.dumps(info,ensure_ascii=False,indent=2),encoding='utf-8')
    MANIFEST.write_text(json.dumps({'project_id':PROJECT.name,'step':19,'status':qa['status'],'info_path':str(INFO),'qa_report_path':str(QA_REPORT),'final_output':str(FINAL)},ensure_ascii=False,indent=2),encoding='utf-8')
    print(time.strftime('%Y-%m-%d %H:%M:%S ')+'DONE '+json.dumps({'status':qa['status'],'final_duration':total,'info':str(INFO)},ensure_ascii=False),flush=True)
    if not passed: raise SystemExit('failed_validation')
if __name__=='__main__': main()
