#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
=============================================================================
THẬP NGŨ NIÊN (THE FIFTEEN SPRINGS) - PROLOGUE 40S FRESH RENDER PIPELINE
=============================================================================
Quy trình sản xuất điện ảnh AI khép kín 40s Prologue hoàn toàn mới:
1. Động cơ Muse2API Gateway (http://127.0.0.1:18610 /v1):
   - Sinh mới 100% qua AI (Text-to-Video & Image-to-Video First-frame).
   - Render tuần tự 4 shots (mỗi shot 10s) theo chuỗi Head-Tail Chaining:
     * Shot 1: Làng quê Bắc Bộ 1980s & Hiên nhà ngói ba gian (start_frame_720p.png)
     * Shot 2: Cận cảnh cụ ông ngâm Kiều (Start Frame = Frame 239 của Shot 1)
     * Shot 3: Vòng xoáy ký ức & Xuyên không (Start Frame = Frame 239 của Shot 2)
     * Shot 4: Khai mở kinh thành Gia Tĩnh triều Minh (Start Frame = Frame 239 của Shot 3 hoặc start_frame_720p.png)
2. Âm thanh Điện ảnh Chuẩn Mực (NO BGM - 100% Pure Voice & Foley):
   - Tuyệt đối KHÔNG ghép nhạc nền nhân tạo (No artificial BGM).
   - Giữ nguyên 100% giọng ngâm Kiều mộc mạc nguyên bản của cụ già và foley tự nhiên.
   - Chuyển cảnh âm thanh: Equal-Power acrossfade (qsin) 0.5s triệt tiêu click/pop.
   - Chuẩn hóa EBU R128 (-14.0 LUFS, True Peak <= -1.0 dBTP, Stereo 48kHz AAC).
3. Xuất bản Master:
   - 06_Exports/thap_ngu_nien_prologue_master_40s.mp4
   - 06_Exports/thap_ngu_nien_ep01_intro_prologue_60s.mp4 (Intro 20s + Prologue 40s)
   - Zero-Waste Contact Sheet & Critic Gate 14 lỗi AI.
=============================================================================
"""

import os
import sys
import time
import shutil
import subprocess
import json
from pathlib import Path
from typing import Optional, Dict, Any, Tuple

import cv2
import requests
import numpy as np

# Đảm bảo UTF-8 trên Windows Console
if sys.platform == "win32":
    try:
        sys.stdout.reconfigure(encoding="utf-8")
        sys.stderr.reconfigure(encoding="utf-8")
    except Exception:
        pass

BASE_DIR = Path("c:/Projects/KieuStory")
PIPELINE_DIR = BASE_DIR / "05_Production_Pipeline"
if str(PIPELINE_DIR) not in sys.path:
    sys.path.insert(0, str(PIPELINE_DIR))

ASSETS_DIR = BASE_DIR / "04_Assets"
VIDEOS_DIR = ASSETS_DIR / "videos"
AUDIO_DIR = ASSETS_DIR / "audio" / "prologue_inspect"
KEYFRAMES_DIR = ASSETS_DIR / "keyframes"
CRITIC_DIR = ASSETS_DIR / "critic_queue"
EXPORTS_DIR = BASE_DIR / "06_Exports"
TEMP_DIR = ASSETS_DIR / "temp_prologue_fresh"

VIDEOS_DIR.mkdir(parents=True, exist_ok=True)
KEYFRAMES_DIR.mkdir(parents=True, exist_ok=True)
CRITIC_DIR.mkdir(parents=True, exist_ok=True)
EXPORTS_DIR.mkdir(parents=True, exist_ok=True)
TEMP_DIR.mkdir(parents=True, exist_ok=True)

from muse_api_client import MuseApiClient
from audio_continuity_engine import AudioContinuityEngine
from contact_sheet_engine import ZeroWasteContactSheet
from antigravity_critic_gate import evaluate_scene_gate

ace = AudioContinuityEngine()


def get_ffmpeg() -> str:
    exe = Path(sys.executable).parent / "ffmpeg.exe"
    if exe.exists():
        return str(exe)
    for p in [
        r"C:\Users\Admin\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe\ffmpeg-9.0.2-full_build\bin\ffmpeg.exe",
        r"C:\Users\Admin\AppData\Local\Microsoft\WindowsApps\ffmpeg.exe",
        "ffmpeg"
    ]:
        if os.path.exists(p) or p == "ffmpeg":
            try:
                if subprocess.run([p, "-version"], capture_output=True).returncode == 0:
                    return p
            except Exception:
                continue
    return "ffmpeg"


def poll_task_with_display(client: MuseApiClient, task_id: str, label: str, timeout: float = 600.0) -> Dict[str, Any]:
    """Theo dõi tiến độ tạo video trên Muse2API với thông báo chi tiết."""
    print(f"      [*] Đang theo dõi tiến độ Muse.ai cho [{label}] (Task ID: {task_id})...")
    started = time.time()
    last_p = -1
    url = f"{client.base_url}/v1/videos/{task_id}"

    while time.time() - started < timeout:
        time.sleep(3.5)
        try:
            resp = requests.get(url, headers=client.headers, timeout=12.0)
            resp.raise_for_status()
            data = resp.json()
        except Exception as e:
            print(f"      [~] Tạm thời mất kết nối tới gateway ({e}), đang thử lại...")
            continue

        status = data.get("status")
        progress = data.get("progress", 0)

        if progress != last_p:
            elapsed = int(time.time() - started)
            print(f"      [{elapsed:03d}s] [{label}] Trạng thái: {status.upper()} ({progress}%)")
            last_p = progress

        if status == "succeeded":
            result = data.get("result") or {}
            video_url = result.get("url")
            if not video_url:
                raise RuntimeError(f"Task thành công nhưng thiếu URL video: {data}")
            return data

        if status == "failed":
            err = data.get("error")
            raise RuntimeError(f"Tạo video thất bại từ Muse2API: {err}")

    raise TimeoutError(f"Hết thời gian chờ tạo video ({timeout}s) cho task {task_id}.")


def render_shot_with_tail(
    client: MuseApiClient,
    shot_id: str,
    label: str,
    prompt: str,
    input_frame_path: Optional[Path],
    duration: int = 10
) -> Tuple[Path, Path]:
    """
    Render 1 shot mới 100% qua Muse2API, tải về và trích xuất tail frame chuẩn bị cho shot sau.
    """
    target_video_path = VIDEOS_DIR / f"{shot_id}_10s_v2.mp4"
    shot_kf_dir = KEYFRAMES_DIR / shot_id
    shot_kf_dir.mkdir(parents=True, exist_ok=True)
    tail_frame_path = shot_kf_dir / "clean_frame_239_v2.jpg"

    print("\n" + "-" * 70)
    print(f"🎬 BẮT ĐẦU SẢN XUẤT: {label} ({shot_id})")
    print(f"   Đích video xuất bản: {target_video_path.name}")
    if input_frame_path and input_frame_path.exists():
        print(f"   Start Frame: {input_frame_path.name} ({input_frame_path.stat().st_size // 1024} KB)")
    else:
        print(f"   Start Frame: Không có (Text-to-Video)")
    print("-" * 70)

    # 1. Gửi task sang Muse2API
    task_id = client.create_video_task(
        prompt=prompt,
        image=str(input_frame_path) if (input_frame_path and input_frame_path.exists()) else None,
        model="sora",
        size="16:9",
        duration=duration
    )
    print(f"   ✓ Đã kích hoạt tác vụ Muse2API: {task_id}")

    # 2. Polling đến khi hoàn tất
    task_data = poll_task_with_display(client, task_id, label, timeout=600.0)
    video_url = task_data["result"]["url"]
    print(f"   ✓ Video đã sẵn sàng trên gateway: {video_url}")

    # 3. Tải video MP4 về VIDEOS_DIR
    client.download_media(video_url, target_video_path)
    print(f"   ✓ Đã tải và lưu video tươi mới: {target_video_path.name} ({target_video_path.stat().st_size // 1024} KB)")

    # 4. Trích xuất Tail Frame (Frame 239) cho Head-Tail Chaining
    cap = cv2.VideoCapture(str(target_video_path))
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    target_pos = max(0, total_frames - 1)
    cap.set(cv2.CAP_PROP_POS_FRAMES, target_pos)
    ret, tail_frame = cap.read()
    cap.release()

    if ret and tail_frame is not None:
        cv2.imwrite(str(tail_frame_path), tail_frame)
        print(f"   ✓ Đã trích xuất Tail Frame (frame #{target_pos}): {tail_frame_path.name}")
    else:
        ffmpeg_bin = get_ffmpeg()
        cmd_tf = [
            ffmpeg_bin, "-y",
            "-sseof", "-0.1",
            "-i", str(target_video_path),
            "-vframes", "1",
            "-q:v", "2",
            str(tail_frame_path)
        ]
        subprocess.run(cmd_tf, check=True, capture_output=True)
        print(f"   ✓ Đã trích xuất Tail Frame qua FFmpeg: {tail_frame_path.name}")

    return target_video_path, tail_frame_path


def main():
    print("=" * 80)
    print("🎬 KIEU STORY - PRODUCTION OF 40S FRESH PROLOGUE VIA MUSE2API GATEWAY")
    print("=" * 80)

    client = MuseApiClient()
    try:
        health = client.check_health()
        print(f"[✓] Muse2API Gateway trực tuyến 100%: {health.get('accounts', {}).get('available', 0)}/{health.get('accounts', {}).get('total', 0)} tài khoản khả dụng.")
    except Exception as e:
        print(f"[!] Không thể kết nối tới Muse2API Gateway ({e}). Vui lòng kiểm tra cổng 18610!")
        sys.exit(1)

    ffmpeg_bin = get_ffmpeg()
    print(f"[✓] Nhị phân FFmpeg: {ffmpeg_bin}")

    # =========================================================================
    # BƯỚC 1: SẢN XUẤT 4 SHOTS TƯƠI MỚI (AI RENDER) THEO CHUỖI HEAD-TAIL
    # =========================================================================

    # --- SHOT 1 ---
    shot01_id = "prologue_shot01"
    shot01_label = "Shot 1: Làng Quê Bắc Bộ 1980s & Hiên Nhà Ngói"
    shot01_prompt = (
        "Bối cảnh làng quê Bắc Bộ Việt Nam những năm 1980 (1980s Northern Vietnamese rural countryside). "
        "Khung cảnh chiều tà thanh bình, hiên ngôi nhà ngói ba gian rêu phong cổ kính, sân gạch đỏ, rặng cau cao vút đung đưa trong gió. "
        "Một cụ ông 70 tuổi hiền từ, tóc râu bạc phơ, mặc áo bà ba nâu sồng mộc mạc ngồi trên chõng tre ở hiên nhà, tay cầm quạt mo cau phe phẩy nhịp nhàng. "
        "Đứa cháu nhỏ khoảng 6 tuổi ngoan ngoãn gối đầu lên đùi ông ngủ thiu thiu thanh thản. "
        "Cụ ông miệng mấp máy ngâm nga nảy Kiều trầm mặc. Ánh hoàng hôn vàng ấm áp rọi qua hàng hiên, máy quay slow cinematic dolly-in, 24fps hoài niệm giàu chất thơ. "
        "Âm thanh: Tiếng quạt mo cau phe phẩy nhịp nhàng, tiếng lá cau xào xạc trong gió chiều, tiếng ve râm ran xa xa, tiếng cụ ông ngâm nga nảy Kiều trầm ấm. "
        "Tuyệt đối KHÔNG sinh nhạc nền, không âm thanh điện tử, không tạp âm rè nhiễu."
    )
    shot01_start_frame = KEYFRAMES_DIR / "prologue_shot1" / "start_frame_720p.png"
    if not shot01_start_frame.exists():
        shot01_start_frame = KEYFRAMES_DIR / "prologue_shot01" / "start_frame_720p.png"

    video01_path, tail01_path = render_shot_with_tail(
        client=client,
        shot_id=shot01_id,
        label=shot01_label,
        prompt=shot01_prompt,
        input_frame_path=shot01_start_frame,
        duration=10
    )

    # --- SHOT 2 ---
    shot02_id = "prologue_shot02"
    shot02_label = "Shot 2: Nỗi Niềm Nảy Kiều & Ánh Mắt Trầm Mặc"
    shot02_prompt = (
        "Tiếp diễn mượt mà từ khung hình cận cảnh: Cận cảnh cụ ông 70 tuổi gương mặt đôn hậu hằn in nếp nhăn năm tháng và đứa cháu gái nhỏ đang ngủ say bình yên trên ngực ông dưới ánh hoàng hôn vàng ấm hắt qua hàng hiên nhà ngói cổ kính. "
        "Bàn tay già nua nhẹ nhàng phe phẩy quạt mo cau. Đôi môi ông mấp máy ngâm nga điệu thơ lục bát Kiều trầm bổng: 'Chữ tài chữ mệnh khéo là ghét nhau; Trải qua một cuộc bể dâu...'. "
        "Ánh mắt ông đăm chiêu, trầm mặc nhìn xa xăm vào khoảng không gian chiều tà, khói lam chiều bảng lảng. "
        "Cú máy slow cinematic push-in dần vào đôi mắt ngập tràn ký ức của người ông, 24fps hoài niệm giàu cảm xúc. "
        "Âm thanh: Giọng cụ ông 70 tuổi ngâm thơ Kiều trầm ấm, mộc mạc: 'Chữ tài chữ mệnh khéo là ghét nhau; Trải qua một cuộc bể dâu...'. Tiếng quạt mo cau thoảng nhẹ, tiếng thở êm của cháu. "
        "Tuyệt đối KHÔNG sinh nhạc nền, không tạp âm rác."
    )
    # Head-Tail Chaining: Start Frame = Tail frame của Shot 1
    shot02_start_frame = tail01_path

    video02_path, tail02_path = render_shot_with_tail(
        client=client,
        shot_id=shot02_id,
        label=shot02_label,
        prompt=shot02_prompt,
        input_frame_path=shot02_start_frame,
        duration=10
    )

    # --- SHOT 3 ---
    shot03_id = "prologue_shot03"
    shot03_label = "Shot 3: Vòng Xoáy Ký Ức & Chuyển Cảnh Xuyên Không"
    shot03_prompt = (
        "Tiếp diễn mượt mà từ khung hình đặc tả đôi mắt: Đặc tả cận cảnh đôi mắt trầm tư đầy nếp nhăn thời gian của cụ ông. "
        "Khi câu ngâm Kiều ngân dài da diết '...Những điều trông thấy mà đau đớn lòng', trong ánh mắt ông phản chiếu ánh hoàng hôn dần biến hóa thành một luồng ánh sáng sương khói huyền ảo như vòng xoáy ký ức (cinematic memory vortex). "
        "Không gian hiên nhà ngói 1980s từ từ tan biến và hòa tan (cross-dissolve) vào làn sương trắng bồng bềnh. Những cánh hoa lê trắng và hoa đào bay lượn trong gió sương, thời gian lùi sâu về quá khứ lịch sử hàng trăm năm trước. "
        "Cuối 10 giây, làn sương hé mở ra bóng dáng mờ ảo của mái ngói lầu các cong vút thời Gia Tĩnh triều Minh. Cú máy macro push-in kết hợp transition hòa tan huyền ảo, 24fps cinematic. "
        "Âm thanh: Tiếng ngâm Kiều ngân dài da diết: '...Những điều trông thấy mà đau đớn lòng', tiếng gió ký ức vi vu, tiếng sương mờ chuyển động. "
        "Tuyệt đối KHÔNG sinh nhạc nền điện tử, không tạp âm rè méo."
    )
    # Head-Tail Chaining: Start Frame = Tail frame của Shot 2
    shot03_start_frame = tail02_path

    video03_path, tail03_path = render_shot_with_tail(
        client=client,
        shot_id=shot03_id,
        label=shot03_label,
        prompt=shot03_prompt,
        input_frame_path=shot03_start_frame,
        duration=10
    )

    # --- SHOT 4 ---
    shot04_id = "prologue_shot04"
    shot04_label = "Shot 4: Khai Mở Kinh Thành Thời Gia Tĩnh Triều Minh"
    shot04_prompt = (
        "Tiếp diễn mượt mà từ khung hình cổng thành lầu các cổ kính: Cú máy bay lướt (cinematic fly-through) bay qua cổng thành lầu các cổ kính thời Gia Tĩnh triều Minh. "
        "Làn sương mù mịt tan biến dưới ánh bình minh xuân rực rỡ, mở ra toàn cảnh đại đô thị kinh thành tráng lệ, cờ ngũ sắc bay phấp phới, dòng người trẩy hội Đạp Thanh tấp nập trong trang phục cổ trang thanh nhã, cành hoa đào hoa lê rợp lối đi bên cầu đá uốn cong trên dòng sông biếc. "
        "Hiện duy nhất dòng chữ phụ đề cổ phong trang trọng: 'Năm Gia Tĩnh, triều Minh'. Không thêm bất kỳ lời giải thích nào khác. Cú máy sweeping crane aerial reveal tráng lệ, 24fps điện ảnh bom tấn. "
        "Âm thanh: Tiếng cờ ngũ sắc bay phần phật trên cổng thành, tiếng rì rầm từ xa của dòng người trẩy hội, tiếng gió xuân lướt qua mái ngói. "
        "Tuyệt đối KHÔNG sinh nhạc nền, không tạp âm rác."
    )
    # Head-Tail Chaining: Start Frame = Tail frame của Shot 3 hoặc keyframe cung đình
    shot04_start_frame = KEYFRAMES_DIR / "prologue_shot4" / "start_frame_720p.png"
    if not shot04_start_frame.exists():
        shot04_start_frame = tail03_path

    video04_path, tail04_path = render_shot_with_tail(
        client=client,
        shot_id=shot04_id,
        label=shot04_label,
        prompt=shot04_prompt,
        input_frame_path=shot04_start_frame,
        duration=10
    )

    print("\n" + "=" * 80)
    print("✓ ĐÃ SẢN XUẤT XONG 4 SHOTS TƯƠI MỚI 100% TỪ MUSE2API GATEWAY!")
    print("=" * 80)

    # =========================================================================
    # BƯỚC 2: TÍCH HỢP ÂM THANH GỐC NGUYÊN BẢN & CHUẨN HÓA TỪNG SHOT (NO BGM)
    # =========================================================================
    print("\n[2/5] Đang tích hợp âm thanh ngâm thơ & foley gốc cho từng shot...")

    audio_sources = [
        AUDIO_DIR / "prologue_shot_01_lang_que_198x_10s.wav",
        AUDIO_DIR / "prologue_shot_02_ngam_kieu_10s.wav",
        AUDIO_DIR / "prologue_shot_03_xuyen_khong_10s.wav",
        AUDIO_DIR / "prologue_shot_04_gia_tinh_trieu_minh_10s.wav",
    ]

    videos = [video01_path, video02_path, video03_path, video04_path]
    prep_shots = []

    for i, (v, a) in enumerate(zip(videos, audio_sources), 1):
        prep_shot = TEMP_DIR / f"fresh_prologue_shot{i:02d}_prep.mp4"
        print(f"   Shot {i}: Muxing video mới ({v.name}) + audio gốc ({a.name})...")
        cmd_mux = [
            ffmpeg_bin, "-y",
            "-ss", "0.0", "-t", "10.0", "-i", str(v),
            "-ss", "0.0", "-t", "10.0", "-i", str(a),
            "-c:v", "libx264", "-crf", "18", "-preset", "slow", "-pix_fmt", "yuv420p",
            "-map", "0:v:0", "-map", "1:a:0",
            "-c:a", "aac", "-b:a", "256k", "-ar", "48000",
            "-shortest",
            str(prep_shot)
        ]
        subprocess.run(cmd_mux, check=True, capture_output=True)
        prep_shots.append(prep_shot)
        print(f"   ✓ Shot {i} hoàn tất chuẩn hóa: {prep_shot.name}")

    # =========================================================================
    # BƯỚC 3: GHÉP NỐI 4 SHOTS THÀNH BẢN MASTER PROLOGUE 40S (XFADE + ACROSSFADE)
    # =========================================================================
    print("\n[3/5] Đang ghép nối liên hoàn 4 shots với xfade 0.5s và acrossfade 0.5s...")

    # Để tổng thời lượng sau 3 lần giao cắt (3 x 0.5s = 1.5s mất đi) đạt chính xác 40.0s,
    # Shot 4 được pad thêm 1.5s ở cuối:
    shot04_padded = TEMP_DIR / "fresh_prologue_shot04_padded.mp4"
    cmd_pad4 = [
        ffmpeg_bin, "-y",
        "-i", str(prep_shots[3]),
        "-filter_complex",
        "[0:v]tpad=stop_mode=clone:stop_duration=1.5[v];"
        "[0:a]apad=pad_dur=1.5,afade=t=out:st=10.0:d=1.5[a]",
        "-map", "[v]", "-map", "[a]",
        "-c:v", "libx264", "-crf", "18", "-preset", "fast", "-pix_fmt", "yuv420p",
        "-c:a", "aac", "-b:a", "256k", "-ar", "48000",
        str(shot04_padded)
    ]
    subprocess.run(cmd_pad4, check=True, capture_output=True)

    raw_prologue_stitched = TEMP_DIR / "prologue_fresh_stitched_raw.mp4"
    filter_prologue = (
        "[0:v][1:v]xfade=transition=fade:duration=0.5:offset=9.5[v01];"
        "[v01][2:v]xfade=transition=fade:duration=0.5:offset=19.0[v02];"
        "[v02][3:v]xfade=transition=fade:duration=0.5:offset=28.5[vmaster];"
        "[0:a][1:a]acrossfade=d=0.5:c1=qsin:c2=qsin[a01];"
        "[a01][2:a]acrossfade=d=0.5:c1=qsin:c2=qsin[a02];"
        "[a02][3:a]acrossfade=d=0.5:c1=qsin:c2=qsin[amaster]"
    )
    cmd_stitch_p = [
        ffmpeg_bin, "-y",
        "-i", str(prep_shots[0]),
        "-i", str(prep_shots[1]),
        "-i", str(prep_shots[2]),
        "-i", str(shot04_padded),
        "-filter_complex", filter_prologue,
        "-map", "[vmaster]", "-map", "[amaster]",
        "-c:v", "libx264", "-crf", "18", "-preset", "slow", "-pix_fmt", "yuv420p",
        "-c:a", "aac", "-b:a", "256k", "-ar", "48000",
        str(raw_prologue_stitched)
    ]
    subprocess.run(cmd_stitch_p, check=True, capture_output=True)
    print(f"   ✓ Ghép nối thành công raw master: {raw_prologue_stitched.name}")

    # =========================================================================
    # BƯỚC 4: CHUẨN HÓA ÂM THANH EBU R128 (-14.0 LUFS) & XUẤT BẢN MASTER
    # =========================================================================
    print("\n[4/5] Đang cân bằng âm lượng EBU R128 (-14.0 LUFS, -1.0 dBTP)...")
    final_prologue_master = EXPORTS_DIR / "thap_ngu_nien_prologue_master_40s.mp4"
    ace.normalize_loudness(
        str(raw_prologue_stitched),
        str(final_prologue_master),
        target_lufs=-14.0,
        two_pass=True
    )
    print(f"   ✓ Đã hoàn tất Prologue Master 40s: {final_prologue_master.name}")

    # Đồng bộ sang VIDEOS_DIR
    shutil.copy2(final_prologue_master, VIDEOS_DIR / "prologue_hoai_niem_198x_to_gia_tinh_40s.mp4")

    # Ghép bản Liên Hoàn 60s (Intro 20s + Prologue 40s)
    intro_master_file = EXPORTS_DIR / "thap_ngu_nien_intro_master_20s.mp4"
    if intro_master_file.exists():
        print("\n   [*] Đang ghép Bản Liên Hoàn 60s (Intro 20s + Prologue 40s)...")
        continuous_60s_export = EXPORTS_DIR / "thap_ngu_nien_intro_prologue_continuous_60s.mp4"
        concat_list_txt = TEMP_DIR / "concat_60s.txt"
        with open(concat_list_txt, "w", encoding="utf-8") as f:
            f.write(f"file '{intro_master_file.as_posix()}'\n")
            f.write(f"file '{final_prologue_master.as_posix()}'\n")

        cmd_60s = [
            ffmpeg_bin, "-y",
            "-f", "concat", "-safe", "0",
            "-i", str(concat_list_txt),
            "-c", "copy",
            str(continuous_60s_export)
        ]
        subprocess.run(cmd_60s, check=True, capture_output=True)
        shutil.copy2(continuous_60s_export, EXPORTS_DIR / "thap_ngu_nien_ep01_intro_prologue_60s.mp4")
        shutil.copy2(continuous_60s_export, VIDEOS_DIR / "thap_ngu_nien_ep01_intro_prologue_60s.mp4")
        print(f"   ✓ Đã xuất bản Bản Liên Hoàn 60s: {continuous_60s_export.name}")

    # =========================================================================
    # BƯỚC 5: KIỂM ĐỊNH CHẤT LƯỢNG (CONTACT SHEET & CRITIC GATE)
    # =========================================================================
    print("\n[5/5] Đang tạo Zero-Waste Contact Sheet và kiểm duyệt Critic Gate...")

    cs_engine = ZeroWasteContactSheet()
    cs_out = CRITIC_DIR / "contact_sheet_prologue_fresh_40s.jpg"
    cs_engine.generate_contact_sheet(
        video_path=str(final_prologue_master),
        output_path=str(cs_out),
        n_frames=12,
        scene_tag="PROLOGUE_FRESH_40S"
    )
    print(f"   ✓ Contact sheet 12 frames đã tạo: {cs_out.name}")

    try:
        verdict = evaluate_scene_gate(
            scene_id="fresh_prologue_40s",
            video_path=str(final_prologue_master),
            contact_sheet_path=str(cs_out)
        )
        print(f"   ✓ Critic Gate verdict: Approved={getattr(verdict, 'approved', None)}, Score={getattr(verdict, 'overall_score', None)}")
    except Exception as e:
        print(f"   [~] Critic Gate evaluation note: {e}")

    # Đo kiểm âm lượng cuối cùng
    final_loud = ace.measure_loudness(str(final_prologue_master))
    print("\n" + "=" * 80)
    print("🎉 HOÀN THÀNH TOÀN DIỆN SẢN XUẤT 40S PROLOGUE TƯƠI MỚI!")
    print("=" * 80)
    print(f"📁 Video Master 40s: {final_prologue_master}")
    print(f"📊 Thông số âm thanh EBU R128:")
    print(f"   - Integrated Loudness: {final_loud.get('input_i')} LUFS (Mục tiêu: -14.0 LUFS)")
    print(f"   - True Peak:          {final_loud.get('input_tp')} dBTP (Mục tiêu <= -1.0 dBTP)")
    print(f"   - Loudness Range:     {final_loud.get('input_lra')} LU")
    print(f"   - Âm thanh:           100% Pure Voice & Foley (Hoàn toàn KHÔNG nhạc nền nhân tạo)")
    print(f"🎞️ 4 Shots tươi mới:    prologue_shot01_10s_v2.mp4 .. prologue_shot04_10s_v2.mp4")
    print("=" * 80 + "\n")


if __name__ == "__main__":
    main()
