"""
Adversarial Stress Test: Diverse Character Transitions and Physical Tail Frame Integrity.
Author: Challenger 1 (Milestone M3 Iteration 3)

Tests:
1. Vương Ông tail frame with Vương Quan shot (ep01_scene02_shot10 -> ep01_scene02_shot11)
2. Kim Trọng tail frame with Thúy Kiều shot (ep01_scene08_shot07 -> ep01_scene08_shot08, ep01_scene10_shot05 -> ep01_scene10_shot06)
3. Thúy Kiều tail frame with Kim Trọng shot (ep01_scene10_shot03 -> ep01_scene10_shot04, ep01_scene10_shot12 -> ep01_scene10_shot13)
4. Thúy Vân tail frame with Thúy Kiều shot (ep01_scene03_shot05 -> ep01_scene03_shot06)
5. Thúy Kiều tail frame with Thúy Vân shot (ep01_scene03_shot01 -> ep01_scene03_shot02)
6. Vương Quan tail frame with Vương Ông shot (ep01_scene02_shot09 -> ep01_scene02_shot10)

Verifies 100% resolve strictly to authentic character portraits without tail frame leakage.
"""

import sys
import shutil
from pathlib import Path
import pytest

PROJECT_ROOT = Path(__file__).resolve().parent.parent
PIPELINE_DIR = PROJECT_ROOT / "05_Production_Pipeline"
KEYFRAMES_DIR = PROJECT_ROOT / "04_Assets" / "keyframes"
CHARACTERS_DIR = PROJECT_ROOT / "04_Assets" / "characters"

if str(PIPELINE_DIR) not in sys.path:
    sys.path.insert(0, str(PIPELINE_DIR))

import production_orchestrator as po


@pytest.fixture(scope="module")
def ep01_shots():
    return po.get_all_shots("ep01")


class TestAdversarialTailFrameCharacterTransitions:
    """Rigorous adversarial probe for physical tail frames across character boundaries."""

    def test_vuong_ong_tail_frame_with_vuong_quan_shot(self, ep01_shots):
        """
        Adversarial Scenario 1:
        ep01_scene02_shot10 (Vương Ông) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene02_shot11 (Vương Quan) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Vương Quan, never tail frame or Vương Ông.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene02_shot10"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_VUONG_ONG_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene02_shot11"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene02_shot10", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "vuong_ong" not in res_lower, f"LEAK: Vương Ông asset resolved for Vương Quan shot: {resolved}"
            assert "vuong_quan" in res_lower, f"Expected Vương Quan portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_kim_trong_tail_frame_with_thuy_kieu_shot(self, ep01_shots):
        """
        Adversarial Scenario 2:
        ep01_scene08_shot07 (Kim Trọng) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene08_shot08 (Thúy Kiều) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Thúy Kiều, never tail frame or Kim Trọng.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene08_shot07"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_KIM_TRONG_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene08_shot08"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene08_shot07", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "kim_trong" not in res_lower, f"LEAK: Kim Trọng asset resolved for Thúy Kiều shot: {resolved}"
            assert "thuy_kieu" in res_lower, f"Expected Thúy Kiều portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_kim_trong_tail_frame_with_thuy_kieu_shot_scene10(self, ep01_shots):
        """
        Adversarial Scenario 2b (Scene 10):
        ep01_scene10_shot05 (Kim Trọng) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene10_shot06 (Thúy Kiều) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Thúy Kiều.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene10_shot05"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_KIM_TRONG_SCENE10_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene10_shot06"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene10_shot05", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "kim_trong" not in res_lower, f"LEAK: Kim Trọng asset resolved for Thúy Kiều shot: {resolved}"
            assert "thuy_kieu" in res_lower, f"Expected Thúy Kiều portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_thuy_kieu_tail_frame_with_kim_trong_shot(self, ep01_shots):
        """
        Adversarial Scenario 3:
        ep01_scene10_shot03 (Thúy Kiều) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene10_shot04 (Kim Trọng) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Kim Trọng, never tail frame or Thúy Kiều.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene10_shot03"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_THUY_KIEU_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene10_shot04"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene10_shot03", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "thuy_kieu" not in res_lower, f"LEAK: Thúy Kiều asset resolved for Kim Trọng shot: {resolved}"
            assert "kim_trong" in res_lower, f"Expected Kim Trọng portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_thuy_kieu_tail_frame_with_kim_trong_shot_scene10_shot13(self, ep01_shots):
        """
        Adversarial Scenario 3b:
        ep01_scene10_shot12 (Thúy Kiều) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene10_shot13 (Kim Trọng) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Kim Trọng.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene10_shot12"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_THUY_KIEU_SCENE10_SHOT12_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene10_shot13"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene10_shot12", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "thuy_kieu" not in res_lower, f"LEAK: Thúy Kiều asset resolved for Kim Trọng shot: {resolved}"
            assert "kim_trong" in res_lower, f"Expected Kim Trọng portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_thuy_van_tail_frame_with_thuy_kieu_shot(self, ep01_shots):
        """
        Adversarial Scenario 4:
        ep01_scene03_shot05 (Thúy Vân) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene03_shot06 (Thúy Kiều) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Thúy Kiều portrait.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene03_shot05"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_THUY_VAN_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene03_shot06"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene03_shot05", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "thuy_van" not in res_lower, f"LEAK: Thúy Vân asset resolved for Thúy Kiều shot: {resolved}"
            assert "thuy_kieu" in res_lower, f"Expected Thúy Kiều portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_thuy_kieu_tail_frame_with_thuy_van_shot(self, ep01_shots):
        """
        Adversarial Scenario 5:
        ep01_scene03_shot01 (Thúy Kiều) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene03_shot02 (Thúy Vân) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Thúy Vân portrait.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene03_shot01"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_THUY_KIEU_TAIL_FRAME_SCENE03")

        try:
            curr_sid = "ep01_scene03_shot02"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene03_shot01", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "thuy_kieu" not in res_lower, f"LEAK: Thúy Kiều asset resolved for Thúy Vân shot: {resolved}"
            assert "thuy_van" in res_lower, f"Expected Thúy Vân portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)

    def test_vuong_quan_tail_frame_with_vuong_ong_shot(self, ep01_shots):
        """
        Adversarial Scenario 6:
        ep01_scene02_shot09 (Vương Quan) leaves a physical clean_frame_239.jpg on disk.
        ep01_scene02_shot10 (Vương Ông) is resolved.
        Must classify as CINEMATIC_CUT and resolve strictly to Vương Ông portrait.
        """
        prev_kf = KEYFRAMES_DIR / "ep01_scene02_shot09"
        prev_kf.mkdir(parents=True, exist_ok=True)
        dummy_tail = prev_kf / "clean_frame_239.jpg"
        dummy_tail.write_bytes(b"ADVERSARIAL_VUONG_QUAN_TAIL_FRAME")

        try:
            curr_sid = "ep01_scene02_shot10"
            curr_data = ep01_shots[curr_sid]

            take_type = po.classify_shot_take(curr_sid, prev_shot_id="ep01_scene02_shot09", shot_data=curr_data)
            assert take_type == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {take_type}"

            resolved, resolved_take = po.resolve_start_frame_v2(curr_sid, curr_data)
            assert resolved is not None, "Resolved frame is None"
            assert resolved_take == "CINEMATIC_CUT", f"Expected CINEMATIC_CUT, got {resolved_take}"

            res_lower = resolved.lower()
            assert "clean_frame_239" not in res_lower, f"LEAK: Consumed adversarial tail frame: {resolved}"
            assert "vuong_quan" not in res_lower, f"LEAK: Vương Quan asset resolved for Vương Ông shot: {resolved}"
            assert "vuong_ong" in res_lower, f"Expected Vương Ông portrait, got: {resolved}"
            assert Path(resolved).exists(), f"Resolved file does not exist: {resolved}"
        finally:
            shutil.rmtree(prev_kf, ignore_errors=True)
