"""
=============================================================================
THẬP NGŨ NIÊN (THE FIFTEEN SPRINGS) - CHALLENGER 2 EMPIRICAL TEST PROBE
=============================================================================
Empirical verification probe for Milestone M1 Gate:
1. Scene Openers (Shot 01 of Scenes 01 to 10):
   - Always classified as CINEMATIC_CUT.
   - Never chain tail frames (no clean_frame_239 / prev_tail).
2. Kim Trọng Shots in Scenes 06 to 10:
   - Verify that resolving Kim Trọng shots NEVER returns Thúy Kiều portrait path.
   - Check normal, empty dict, and adversarial corrupted asset_ref inputs.
3. Vương Quan & Vương Ông Shots in Scenes 01 to 10:
   - Verify that resolving Vương Quan and Vương Ông shots NEVER returns Thúy Kiều portrait path.
4. Prompt Synchronization Consistency:
   - Verify consistency between episodes/ep01/prompts/muse_prompts.json and 02_AI_Prompts/muse_ai_video_prompts.json.
=============================================================================
"""

import os
import sys
import json
from pathlib import Path
import pytest

PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT / "05_Production_Pipeline"))

import production_orchestrator as po

MUSE_EP01_FILE = PROJECT_ROOT / "episodes" / "ep01" / "prompts" / "muse_prompts.json"
BANANA_FILE = PROJECT_ROOT / "02_AI_Prompts" / "gemini_banana_prompts.json"
MUSE_MASTER_FILE = PROJECT_ROOT / "02_AI_Prompts" / "muse_ai_video_prompts.json"

@pytest.fixture(scope="module")
def ep01_data():
    with open(MUSE_EP01_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data.get("motion_prompts", {})

@pytest.fixture(scope="module")
def banana_data():
    with open(BANANA_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data.get("ep01_start_frames", {})

@pytest.fixture(scope="module")
def master_muse_data():
    with open(MUSE_MASTER_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data.get("motion_prompts", {})


class TestSceneOpeners:
    """Scene openers (Shot 01) across Scenes 01 to 10 must always be CINEMATIC_CUT and never chain tail frames."""

    @pytest.mark.parametrize("scene_num", range(1, 11))
    def test_scene_opener_is_cinematic_cut_and_no_tail(self, scene_num, ep01_data):
        shot_id = f"ep01_scene{scene_num:02d}_shot01"
        sdata = ep01_data.get(shot_id, {})
        
        # 1. Classification check
        take_type = po.classify_shot_take(shot_id, shot_data=sdata)
        assert take_type == "CINEMATIC_CUT", f"{shot_id} must be CINEMATIC_CUT, got {take_type}"

        # 2. Start frame resolution check
        resolved_path, classified_take = po.resolve_start_frame_v2(shot_id, sdata)
        assert classified_take == "CINEMATIC_CUT", f"{shot_id} classified as {classified_take}"
        
        if resolved_path:
            norm_path = resolved_path.lower()
            assert "clean_frame_239" not in norm_path, (
                f"{shot_id} illegally chained tail frame: {resolved_path}"
            )
            assert f"ep01_scene{scene_num:02d}_shot00" not in norm_path


class TestKimTrongSafeguardScenes06to10:
    """Empirical verification of Kim Trọng shots across Scenes 06 to 10."""

    # Explicit Kim Trọng shots from screenplay / prompt titles
    SCENE07_KIM_TRONG = [
        "ep01_scene07_shot01", "ep01_scene07_shot03", "ep01_scene07_shot04",
        "ep01_scene07_shot05", "ep01_scene07_shot06", "ep01_scene07_shot08"
    ]
    SCENE08_KIM_TRONG = [
        "ep01_scene08_shot04", "ep01_scene08_shot05", "ep01_scene08_shot07"
    ]
    SCENE09_KIM_TRONG = [
        "ep01_scene09_shot01", "ep01_scene09_shot04"
    ]
    SCENE10_KIM_TRONG = [
        "ep01_scene10_shot04", "ep01_scene10_shot05", "ep01_scene10_shot13"
    ]

    @pytest.mark.parametrize("shot_id", SCENE07_KIM_TRONG + SCENE08_KIM_TRONG + SCENE09_KIM_TRONG + SCENE10_KIM_TRONG)
    def test_canonical_kim_trong_shots_never_return_thuy_kieu(self, shot_id, ep01_data):
        sdata = ep01_data.get(shot_id, {})
        resolved, take_type = po.resolve_start_frame_v2(shot_id, sdata)
        if resolved:
            assert "thuy_kieu" not in resolved.lower(), (
                f"LEAK DETECTED: Kim Trọng shot {shot_id} resolved to Thúy Kiều portrait: {resolved}"
            )
            assert "kim_trong" in resolved.lower(), (
                f"Kim Trọng shot {shot_id} expected Kim Trọng portrait, got: {resolved}"
            )

    @pytest.mark.parametrize("shot_id", ["ep01_scene07_shot01", "ep01_scene09_shot04", "ep01_scene10_shot05", "ep01_scene10_shot13"])
    def test_adversarial_thuy_kieu_corruption_overridden(self, shot_id):
        adversarial_sdata = {
            "character_asset_ref": "04_Assets/characters/01_Main_Protagonists/thuy_kieu_maiden_16yo_720p.png"
        }
        resolved, take_type = po.resolve_start_frame_v2(shot_id, adversarial_sdata)
        assert resolved is not None
        assert "thuy_kieu" not in resolved.lower(), (
            f"Adversarial leak: {shot_id} returned Thúy Kiều: {resolved}"
        )
        assert "kim_trong" in resolved.lower()

    # The probe documents the known Scene 06 misalignment failure modes
    SCENE06_KIM_TRONG_SUSPECTS = [
        "ep01_scene06_shot03", "ep01_scene06_shot04", "ep01_scene06_shot06",
        "ep01_scene06_shot10", "ep01_scene06_shot12", "ep01_scene06_shot14"
    ]

    def test_scene06_kim_trong_anomaly_audit(self, ep01_data):
        """
        Adversarial audit of Scene 06 Kim Trọng shots where muse prompts lack character_asset_ref
        causing fallback to mismatched banana prompt anchors.
        Remediated: All 6 Kim Trọng shots must now resolve to Kim Trọng with 0 anomalies.
        """
        anomalies = []
        for sid in self.SCENE06_KIM_TRONG_SUSPECTS:
            sdata = ep01_data.get(sid, {})
            resolved, take = po.resolve_start_frame_v2(sid, sdata)
            if resolved and "thuy_kieu" in resolved.lower():
                anomalies.append((sid, "RETURNS_THUY_KIEU", resolved))
            elif resolved and "vuong_quan" in resolved.lower() and "Kim Trọng" in sdata.get("scene_title", ""):
                anomalies.append((sid, "RETURNS_VUONG_QUAN", resolved))
            elif not resolved or "kim_trong" not in resolved.lower():
                anomalies.append((sid, "NOT_KIM_TRONG", resolved))
        
        # Verify 0 anomalies: All Kim Trọng shots in Scene 06 resolve to Kim Trọng
        assert len(anomalies) == 0, f"Expected 0 anomalies in Scene 06 Kim Trọng shots, found: {anomalies}"


class TestVuongFamilySafeguard:
    """Empirical verification of Vương Quan and Vương Ông shots across Scenes 01 to 10."""

    VUONG_ONG_SOLO_SHOTS = [
        "ep01_scene02_shot04", "ep01_scene02_shot07", "ep01_scene02_shot10", "ep01_scene02_shot13"
    ]
    VUONG_QUAN_SOLO_SHOTS_CLEAN = [
        "ep01_scene02_shot08", "ep01_scene02_shot09", "ep01_scene02_shot12", "ep01_scene02_shot15",
        "ep01_scene05_shot12"
    ]

    @pytest.mark.parametrize("shot_id", VUONG_ONG_SOLO_SHOTS)
    def test_vuong_ong_shots_never_return_thuy_kieu(self, shot_id, ep01_data):
        sdata = ep01_data.get(shot_id, {})
        resolved, take_type = po.resolve_start_frame_v2(shot_id, sdata)
        assert resolved is not None
        assert "thuy_kieu" not in resolved.lower(), (
            f"Vương Ông shot {shot_id} resolved to Thúy Kiều: {resolved}"
        )
        assert "vuong_ong" in resolved.lower(), f"Expected Vương Ông, got {resolved}"

    @pytest.mark.parametrize("shot_id", VUONG_QUAN_SOLO_SHOTS_CLEAN)
    def test_vuong_quan_clean_shots_never_return_thuy_kieu(self, shot_id, ep01_data):
        sdata = ep01_data.get(shot_id, {})
        resolved, take_type = po.resolve_start_frame_v2(shot_id, sdata)
        assert resolved is not None
        assert "thuy_kieu" not in resolved.lower(), (
            f"Vương Quan shot {shot_id} resolved to Thúy Kiều: {resolved}"
        )
        assert "vuong_quan" in resolved.lower(), f"Expected Vương Quan, got {resolved}"

    def test_vuong_quan_scene05_uncovered_defects(self, ep01_data):
        """
        Audit Scene 05 shots 16 and 24 (Vương Quan storytelling and anti-tears).
        Remediated: Both shots now have character_asset_ref set to vuong_quan, resulting in 0 defects.
        """
        defects = []
        for sid in ["ep01_scene05_shot16", "ep01_scene05_shot24"]:
            sdata = ep01_data.get(sid, {})
            resolved, take = po.resolve_start_frame_v2(sid, sdata)
            if resolved and "thuy_kieu" in resolved.lower():
                defects.append((sid, resolved))
            elif not resolved or "vuong_quan" not in resolved.lower():
                defects.append((sid, f"Expected vuong_quan, got {resolved}"))
        
        assert len(defects) == 0, f"Expected 0 defects in Scene 05 Vuong Quan shots, found: {defects}"


class TestPromptSynchronization:
    """Verify synchronization between ep01/prompts/muse_prompts.json and master prompts."""

    def test_ep01_to_master_prompts_consistency(self, ep01_data, master_muse_data):
        """All ep01 motion prompts must exist in master muse_ai_video_prompts.json and match."""
        for sid, sdata in ep01_data.items():
            assert sid in master_muse_data, f"Shot {sid} missing from master prompts!"
            master_sdata = master_muse_data[sid]
            # Key fields must match
            assert sdata.get("scene_title") == master_sdata.get("scene_title")
            assert sdata.get("character_asset_ref") == master_sdata.get("character_asset_ref")

    def test_kim_trong_11_fixes_present_in_master(self, master_muse_data):
        """Verify the canonical Kim Trọng and Thúy Kiều character_asset_ref in the synced master."""
        # Kim Trọng canonical shots:
        kim_shots = [
            "ep01_scene09_shot04",
            "ep01_scene10_shot04",
            "ep01_scene10_shot05",
            "ep01_scene10_shot07",
            "ep01_scene10_shot08",
            "ep01_scene10_shot10",
            "ep01_scene10_shot13"
        ]
        for sid in kim_shots:
            sdata = master_muse_data.get(sid, {})
            ref = sdata.get("character_asset_ref", "")
            assert "kim_trong" in ref.lower(), f"Master shot {sid} does not have kim_trong ref: {ref}"
            assert "thuy_kieu" not in ref.lower(), f"Master shot {sid} leaked thuy_kieu: {ref}"

        # Thúy Kiều canonical solo / climax pipa shots in Scene 10:
        kieu_shots = [
            "ep01_scene10_shot03",
            "ep01_scene10_shot06",
            "ep01_scene10_shot09",
            "ep01_scene10_shot11",
            "ep01_scene10_shot12"
        ]
        for sid in kieu_shots:
            sdata = master_muse_data.get(sid, {})
            ref = sdata.get("character_asset_ref", "")
            assert "thuy_kieu" in ref.lower(), f"Master shot {sid} does not have thuy_kieu ref: {ref}"
            assert "kim_trong" not in ref.lower(), f"Master shot {sid} leaked kim_trong: {ref}"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
