#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Verification Script for Reviewer 2 - Milestone M4 Gate Verification
Tests:
1. Canonical scenes (10 scenes, 140 shots = 23m20s)
2. resolve_scene_master candidate prioritization
3. Output path hierarchy (06_Exports/ep01_full_feature_master_v1.mp4)
4. assemble_ep01_feature.py support for 10 scenes and _master_audio
5. CLI argument execution (--assemble-episode, --scenes, --check, --dry-run)
6. System Invariants (characters, archive, test suites)
"""

import sys
import os
import json
import re
import subprocess
from pathlib import Path
from PIL import Image

PROJECT_ROOT = Path(__file__).resolve().parents[3]
PIPELINE_DIR = PROJECT_ROOT / "05_Production_Pipeline"
EXPORTS_DIR = PROJECT_ROOT / "06_Exports"
VIDEOS_DIR = PROJECT_ROOT / "04_Assets" / "videos"
CHARACTERS_DIR = PROJECT_ROOT / "04_Assets" / "characters"
ARCHIVE_DIR = PROJECT_ROOT / "04_Assets" / "archive" / "ep01_legacy_v1"

sys.path.insert(0, str(PROJECT_ROOT))
sys.path.insert(0, str(PIPELINE_DIR))
import production_orchestrator as po
import assemble_ep01_feature as assembler

def run_tests():
    results = {}
    print("=" * 80)
    print("INDEPENDENT VERIFICATION — REVIEWER 2 (MILESTONE M4)")
    print("=" * 80)

    # 1. Ep01 Canonical Scenes and Shots
    print("\n[1] Checking 10 Canonical Scenes and 140 Shots of Ep01...")
    canonical = po.EP01_CANONICAL_SCENES
    assert len(canonical) == 10, f"Expected 10 canonical scenes, got {len(canonical)}"
    assert canonical == [f"ep01_scene{i:02d}" for i in range(1, 11)]

    muse_prompts_file = PROJECT_ROOT / "episodes" / "ep01" / "prompts" / "muse_prompts.json"
    with open(muse_prompts_file, "r", encoding="utf-8") as f:
        prompts_data = json.load(f)
    motion_prompts = prompts_data.get("motion_prompts", {})

    total_shots = 0
    shots_per_scene = {}
    for sc in canonical:
        count = sum(1 for shot_id in motion_prompts if shot_id.startswith(sc))
        shots_per_scene[sc] = count
        total_shots += count

    assert total_shots == 140, f"Expected 140 shots across 10 scenes, got {total_shots}"
    duration_s = total_shots * 10
    duration_m = duration_s / 60
    print(f"  -> {canonical[0]} to {canonical[-1]}: {total_shots} shots = {duration_s}s ({duration_m:.2f} min)")
    results["canonical_scenes_10"] = "PASS"
    results["total_shots_140"] = "PASS"

    # 2. Candidate Prioritization in resolve_scene_master
    print("\n[2] Checking resolve_scene_master candidate prioritization...")
    test_scene = "ep01_scene_test_prio"
    import tempfile
    with tempfile.TemporaryDirectory() as tmp_dir:
        tmp_path = Path(tmp_dir)
        tmp_exports = tmp_path / "exports"
        tmp_videos = tmp_path / "videos"
        tmp_exports.mkdir()
        tmp_videos.mkdir()

        f_vid = tmp_videos / f"{test_scene}_master_v1.mp4"
        f_exp_reg = tmp_exports / f"{test_scene}_master_v1.mp4"
        f_cin = tmp_exports / f"{test_scene}_cinematic_master_v1.mp4"
        f_aud = tmp_exports / f"{test_scene}_master_audio_v1.mp4"
        f_aud_v2 = tmp_exports / f"{test_scene}_master_audio_v2.mp4"

        for f in [f_vid, f_exp_reg, f_cin, f_aud, f_aud_v2]:
            f.write_text("dummy video content larger than 1000 bytes " * 30)

        orig_exp, orig_vid = po.EXPORTS_DIR, po.VIDEOS_DIR
        try:
            po.EXPORTS_DIR = tmp_exports
            po.VIDEOS_DIR = tmp_videos

            # Test 1: v2 master_audio beats v1 master_audio
            best = po.resolve_scene_master(test_scene)
            assert best == f_aud_v2, f"Expected {f_aud_v2.name}, got {best.name if best else None}"

            # Test 2: master_audio beats cinematic_master
            f_aud_v2.unlink()
            best = po.resolve_scene_master(test_scene)
            assert best == f_aud, f"Expected {f_aud.name}, got {best.name if best else None}"

            # Test 3: cinematic_master beats export regular master
            f_aud.unlink()
            best = po.resolve_scene_master(test_scene)
            assert best == f_cin, f"Expected {f_cin.name}, got {best.name if best else None}"

            # Test 4: export regular master beats videos master
            f_cin.unlink()
            best = po.resolve_scene_master(test_scene)
            assert best == f_exp_reg, f"Expected {f_exp_reg.name}, got {best.name if best else None}"

            # Test 5: videos master is fallback
            f_exp_reg.unlink()
            best = po.resolve_scene_master(test_scene)
            assert best == f_vid, f"Expected {f_vid.name}, got {best.name if best else None}"
        finally:
            po.EXPORTS_DIR = orig_exp
            po.VIDEOS_DIR = orig_vid

    print("  -> Priority hierarchy: _master_audio_v* (4) > _cinematic_master_v* (3) > _master_v* (2) > videos _master (1) VERIFIED")
    results["resolve_scene_master_prioritization"] = "PASS"

    # 3. Output Path Hierarchy
    print("\n[3] Checking Output Path Hierarchy...")
    expected_output = EXPORTS_DIR / "ep01_full_feature_master_v1.mp4"
    res_dry = po.assemble_episode_master(episode_id="ep01", dry_run=True)
    assert res_dry == expected_output, f"Expected {expected_output}, got {res_dry}"
    print(f"  -> Default output path correctly resolves to: {expected_output}")
    results["output_path_hierarchy"] = "PASS"

    # 4. assemble_ep01_feature.py
    print("\n[4] Checking assemble_ep01_feature.py support for 10 scenes and _master_audio...")
    parts_10 = assembler.get_ep01_assembly_parts(num_scenes=10)
    assert len(parts_10) == 10, f"Expected 10 parts, got {len(parts_10)}"
    for tag, p, _ in parts_10:
        assert tag in canonical
        assert "master_audio" in p.name
    print("  -> assemble_ep01_feature.py correctly targets 10 scenes and _master_audio candidates")
    results["assemble_ep01_feature_10_scenes"] = "PASS"

    # 5. CLI Arguments Execution
    print("\n[5] Checking CLI flags execution...")
    # Helper to run subprocess safely with utf-8
    def run_cli(cmd):
        return subprocess.run(cmd, capture_output=True, encoding="utf-8", errors="replace")

    # --assemble-episode ep01 --check
    cmd_check = [sys.executable, str(PIPELINE_DIR / "production_orchestrator.py"), "--assemble-episode", "ep01", "--check"]
    res = run_cli(cmd_check)
    assert res.returncode == 0
    assert "10 Scenes" in res.stdout
    assert "ep01_full_feature_master_v1.mp4" in res.stdout

    # --assemble-episode ep01 --dry-run
    cmd_dry = [sys.executable, str(PIPELINE_DIR / "production_orchestrator.py"), "--assemble-episode", "ep01", "--dry-run"]
    res = run_cli(cmd_dry)
    assert res.returncode == 0
    assert "Chế độ mô phỏng (--dry-run)" in res.stdout

    # --assemble-episode ep01 --scenes ep01_scene01 ep01_scene02 --check
    cmd_scenes = [sys.executable, str(PIPELINE_DIR / "production_orchestrator.py"), "--assemble-episode", "ep01", "--scenes", "ep01_scene01", "ep01_scene02", "--check"]
    res = run_cli(cmd_scenes)
    assert res.returncode == 0
    assert "2 Scenes" in res.stdout

    # assemble_ep01_feature.py --check
    cmd_feat_check = [sys.executable, str(PIPELINE_DIR / "assemble_ep01_feature.py"), "--check"]
    res = run_cli(cmd_feat_check)
    assert res.returncode == 0
    assert "10 Cảnh" in res.stdout

    print("  -> CLI arguments (--assemble-episode, --scenes, --check, --dry-run) VERIFIED")
    results["cli_arguments"] = "PASS"

    # 6. System Invariants
    print("\n[6] Checking System Invariants...")
    # 6.1 Archive ep01_legacy_v1
    archive_files = [p for p in ARCHIVE_DIR.glob("*.mp4") if p.is_file()]
    assert len(archive_files) == 184, f"Expected 184 archived videos, found {len(archive_files)}"
    assert (ARCHIVE_DIR / "README.md").exists(), "Archive README.md missing"
    readme_text = (ARCHIVE_DIR / "README.md").read_text(encoding="utf-8")
    assert "Điều 43" in readme_text and "NGUYỄN SĨ SƠN" in readme_text
    print(f"  -> Archive ep01_legacy_v1: {len(archive_files)} files intact, README.md verified")
    results["archive_invariant"] = "PASS"

    # 6.2 Character portraits
    from tests.test_m2_hygiene import CORE_EP01_CHARACTERS
    for rel_path, expected_dims in CORE_EP01_CHARACTERS:
        full_path = CHARACTERS_DIR / rel_path
        assert full_path.exists(), f"Missing core portrait: {rel_path}"
        with Image.open(full_path) as img:
            assert img.format == "PNG"
            assert img.size == expected_dims
    print("  -> All 14 core character portraits verified 100% intact with correct dimensions")
    results["14_core_portraits"] = "PASS"

    # 6.3 Total character files check (Invariant Gate)
    tracked_res = run_cli(["git", "ls-files", "04_Assets/characters/"])
    tracked_count = len(tracked_res.stdout.strip().splitlines())
    total_files = sum(len(files) for _, _, files in os.walk(CHARACTERS_DIR))
    print(f"  -> Tracked character assets in git: {tracked_count}")
    print(f"  -> Total physical files in 04_Assets/characters/: {total_files}")
    if total_files != 62:
        print(f"  [!] INVARIANT MISMATCH: Expected 62, found {total_files} (34 untracked turnaround sheets present)")
        results["character_total_files_62_invariant"] = f"FAIL (found {total_files})"
    else:
        results["character_total_files_62_invariant"] = "PASS"

    print("\n" + "=" * 80)
    print("VERIFICATION SUMMARY:")
    for k, v in results.items():
        print(f"  {k:<40}: {v}")
    print("=" * 80)
    return results

if __name__ == "__main__":
    run_tests()
