import os
import re
import sys
import json

# Ensure UTF-8 output on Windows
sys.stdout.reconfigure(encoding='utf-8')

def run_adversarial_tests():
    base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
    tap01_path = os.path.join(base_dir, 'FilmMaker', 'TAP_01_XUAN_SAC_THE_NGUYEN_VA_GIONG_BAO_DOAN_TRUONG.md')
    idx_path = os.path.join(base_dir, 'FilmMaker', 'INDEX_VA_DANH_MUC_CANH_QUAY.md')
    sb_path = os.path.join(base_dir, '03_Storyboards', 'Ep01_Gia_Bien_Storyboard.md')
    epguide_path = os.path.join(base_dir, '01_Scripts_And_Episodes', 'EPISODE_GUIDE_30MIN.md')

    with open(tap01_path, 'r', encoding='utf-8') as f:
        tap01 = f.read()
    with open(idx_path, 'r', encoding='utf-8') as f:
        idx = f.read()
    with open(sb_path, 'r', encoding='utf-8') as f:
        sb = f.read()
    with open(epguide_path, 'r', encoding='utf-8') as f:
        epguide = f.read()

    results = {
        "shot_continuity": {},
        "male_tears": {},
        "cross_file_consistency": {},
        "failures": []
    }

    # =========================================================================
    # TEST 1: SHOT ID CONTINUITY & UNIQUENESS ACROSS ALL 188 SHOTS
    # =========================================================================
    print("=== TEST 1: SHOT ID CONTINUITY & UNIQUENESS ===")
    
    # Match all shot headers: ##### Shot YY: `ep01_sceneXX_shotYY` (10s)
    shot_header_pattern = re.compile(r'#####\s+Shot\s+(\d+):\s+`?(ep01_scene(\d+)_shot(\d+))`?\s+\((\d+)s\)', re.IGNORECASE)
    matches = list(shot_header_pattern.finditer(tap01))
    
    total_shots = len(matches)
    print(f"Total shot headings found: {total_shots}")
    
    expected_scene_counts = {
        1: 21, 2: 15, 3: 15, 4: 12,
        5: 27, 6: 14, 7: 8, 8: 8, 9: 6,
        10: 14, 11: 10, 12: 8, 13: 12, 14: 12, 15: 6
    }
    expected_total_shots = sum(expected_scene_counts.values()) # 188
    
    if total_shots != expected_total_shots:
        results["failures"].append(f"Shot count mismatch: expected {expected_total_shots}, got {total_shots}")

    # Check uniqueness
    shot_ids = [m.group(2) for m in matches]
    unique_shot_ids = set(shot_ids)
    if len(shot_ids) != len(unique_shot_ids):
        duplicates = [s for s in unique_shot_ids if shot_ids.count(s) > 1]
        results["failures"].append(f"Duplicate shot IDs found: {duplicates}")

    # Check strict sequential continuity
    expected_shot_sequence = []
    for sc in range(1, 16):
        cnt = expected_scene_counts[sc]
        for sh in range(1, cnt + 1):
            expected_shot_sequence.append(f"ep01_scene{sc:02d}_shot{sh:02d}")

    if shot_ids != expected_shot_sequence:
        results["failures"].append(f"Shot ID sequence does not match expected strictly contiguous sequence!")
        # Find differences
        for i, (actual, exp) in enumerate(zip(shot_ids, expected_shot_sequence)):
            if actual != exp:
                results["failures"].append(f"Mismatch at index {i}: expected {exp}, got {actual}")
                break

    # Check heading numbers match the shot IDs
    for m in matches:
        heading_num = int(m.group(1))
        shot_id = m.group(2)
        scene_num_in_id = int(m.group(3))
        shot_num_in_id = int(m.group(4))
        duration = int(m.group(5))
        
        if heading_num != shot_num_in_id:
            results["failures"].append(f"Heading number {heading_num} does not match shot ID {shot_id}")
        if duration != 10:
            results["failures"].append(f"Duration for {shot_id} is {duration}s instead of 10s")

    # Check if there are any other occurrences of ep01_scene\d+_shot\d+ that are NOT in expected_shot_sequence
    all_shot_refs = re.findall(r'ep01_scene\d+_shot\d+', tap01)
    invalid_refs = [s for s in all_shot_refs if s not in unique_shot_ids]
    if invalid_refs:
        results["failures"].append(f"Found invalid shot ID references in tap01: {set(invalid_refs)}")

    results["shot_continuity"] = {
        "total_shots_found": total_shots,
        "unique_shots": len(unique_shot_ids),
        "strictly_contiguous": shot_ids == expected_shot_sequence,
        "all_durations_10s": all(int(m.group(5)) == 10 for m in matches),
        "scene_counts": {sc: len([s for s in shot_ids if f"scene{sc:02d}" in s]) for sc in range(1, 16)}
    }
    print(f"Shot continuity status: {results['shot_continuity']['strictly_contiguous']}")
    print(f"Scene breakdown: {results['shot_continuity']['scene_counts']}")

    # =========================================================================
    # TEST 2: ADVERSARIAL MALE TEARS / WEEPING ANALYSIS
    # =========================================================================
    print("\n=== TEST 2: ADVERSARIAL MALE TEARS & WEEPING ANALYSIS ===")

    male_characters = [
        "Kim Trọng", "Kim sinh", "chàng Kim", "Kim", 
        "Vương Quan", "Quan", 
        "Vương Ông", "Vương viên ngoại", "Viên ngoại", "cụ ông", "người cha", "cha Kiều"
    ]

    weeping_keywords = [
        "khóc", "rơi lệ", "giọt lệ", "ngấn lệ", "đầm đìa", "châu sa", 
        "rơi nước mắt", "nước mắt", "nấc nghẹn", "sụt sùi", "thút thít", 
        "ứa lệ", "hoen lệ", "hàng lệ", "dòng lệ", "lệ rơi", "tuôn lệ", "lệ tràn"
    ]

    # Split TAP_01 into sections / shots to analyze context
    # Let's split by shot headings
    shot_splits = shot_header_pattern.split(tap01)
    # shot_splits: [preamble, heading_num, shot_id, scene_num, shot_num, duration, content, ...]
    
    preamble = shot_splits[0]
    shot_blocks = []
    i = 1
    while i < len(shot_splits):
        heading_num = shot_splits[i]
        shot_id = shot_splits[i+1]
        scene_num = shot_splits[i+2]
        shot_num = shot_splits[i+3]
        duration = shot_splits[i+4]
        content = shot_splits[i+5]
        shot_blocks.append((shot_id, content))
        i += 6

    print(f"Parsed {len(shot_blocks)} shot content blocks for character action analysis.")

    # Check preamble (character roster, prologue, notes)
    print("Checking character roster and intro...")
    # Find all sentences with weeping keywords
    weeping_findings = []

    def check_text_for_male_tears(source_name, text):
        findings = []
        # Split text into sentences or lines
        lines = text.split('\n')
        for line_no, line in enumerate(lines, 1):
            line_lower = line.lower()
            for kw in weeping_keywords:
                if kw in line_lower:
                    # Found a weeping keyword on this line! Check if male character is associated
                    # Look at context: line itself, or surrounding lines
                    for mc in male_characters:
                        # Regex match for male character name to avoid false substrings (e.g. "quan" in "quan tài" or "liên quan" or "quan lại")
                        if mc in ["Quan", "Kim", "Ông"]:
                            # Word boundary check and exclude common false positives
                            pattern = r'(?<![a-zA-Z0-9_\u00C0-\u024F\u1EA0-\u1EF9])' + re.escape(mc) + r'(?![a-zA-Z0-9_\u00C0-\u024F\u1EA0-\u1EF9])'
                        else:
                            pattern = re.escape(mc)
                        
                        if re.search(pattern, line, re.IGNORECASE):
                            # Filter out false positives like "viên quan sai nha", "sai nha quan lại", "tổng quan"
                            if mc == "Quan":
                                if re.search(r'(viên quan|quan nha|quan lại|sai nha|tổng quan|quan tài|chức quan)', line, re.IGNORECASE) and not re.search(r'Vương Quan', line, re.IGNORECASE):
                                    continue
                            if mc == "Ông" and ("Vương Ông" not in line and "cụ ông" not in line and "ông bà" not in line):
                                pass

                            findings.append({
                                "source": source_name,
                                "line_no": line_no,
                                "male_character": mc,
                                "keyword": kw,
                                "line": line.strip()
                            })
        return findings

    # Check preamble
    preamble_findings = check_text_for_male_tears("Preamble", preamble)
    print(f"Preamble candidates: {len(preamble_findings)}")
    for f in preamble_findings:
        print(f"  [Preamble] {f['male_character']} + '{f['keyword']}': {f['line']}")

    # Check each shot block
    shot_findings = []
    for shot_id, content in shot_blocks:
        findings = check_text_for_male_tears(shot_id, content)
        if findings:
            shot_findings.extend(findings)

    print(f"\nShot blocks candidates: {len(shot_findings)}")
    for f in shot_findings:
        print(f"  [{f['source']}] {f['male_character']} + '{f['keyword']}': {f['line']}")

    # Let's inspect each candidate deeply to determine if it is:
    # A) A male character crying/weeping (VIOLATION)
    # B) Anti-crying directive text (e.g. "tuyệt đối không rơi lệ", "0% nước mắt") (PASS)
    # C) Another character (e.g. Thúy Kiều) crying while male character is present or observing (PASS)
    # D) Figurative usage (e.g. "ngấn dòng suy tưởng") (PASS)

    violations = []
    for f in preamble_findings + shot_findings:
        line_text = f["line"]
        mc = f["male_character"]
        kw = f["keyword"]
        src = f["source"]
        
        # Check if line contains negation: "không khóc", "không rơi lệ", "tuyệt đối 0% nước mắt", "tuyệt đối không", v.v.
        if re.search(r'(tuyệt đối không|không rơi lệ|không khóc|0% nước mắt|không gào khóc|tuyệt đối 0%)', line_text, re.IGNORECASE):
            # This is an anti-male-tears directive!
            continue
            
        # Check if Kiều, Thúy Vân, Vương Bà, Đạm Tiên is the subject of crying
        # e.g., "Thúy Kiều rơi lệ", "Kiều ngấn lệ", "Kiều quỳ sụp khóc thương"
        # Let's check who the keyword belongs to
        print(f"\nAnalyzing candidate in {src}:")
        print(f"  Line: {line_text}")
        print(f"  Male: {mc}, Keyword: {kw}")

    # =========================================================================
    # TEST 3: EXHAUSTIVE SCAN FOR ANY WEEPING KEYWORDS IN TAP_01
    # =========================================================================
    print("\n=== TEST 3: EXHAUSTIVE SCAN FOR ALL WEEPING KEYWORDS IN ENTIRE TAP_01 ===")
    all_weeping_occurrences = []
    lines = tap01.split('\n')
    for line_idx, line in enumerate(lines, 1):
        for kw in weeping_keywords:
            if kw in line.lower():
                all_weeping_occurrences.append((line_idx, kw, line.strip()))

    print(f"Total lines in TAP_01 with any weeping keyword: {len(all_weeping_occurrences)}")
    for l_idx, kw, line in all_weeping_occurrences:
        # Check who is crying in this line
        print(f"  Line {l_idx} [{kw}]: {line[:120]}...")

    # =========================================================================
    # TEST 4: CHECK OTHER RELEVANT FILES TOUCHED BY WORKER
    # =========================================================================
    print("\n=== TEST 4: CHECKING STORYBOARD, EPISODE GUIDE, AND INDEX ===")
    sb_findings = check_text_for_male_tears("Ep01_Gia_Bien_Storyboard.md", sb)
    epguide_findings = check_text_for_male_tears("EPISODE_GUIDE_30MIN.md", epguide)
    idx_findings = check_text_for_male_tears("INDEX_VA_DANH_MUC_CANH_QUAY.md", idx)

    print(f"Storyboard findings: {len(sb_findings)}")
    for f in sb_findings:
        print(f"  [Storyboard] {f['male_character']} + '{f['keyword']}': {f['line']}")

    print(f"Episode Guide findings: {len(epguide_findings)}")
    for f in epguide_findings:
        print(f"  [EPGuide] {f['male_character']} + '{f['keyword']}': {f['line']}")

    print(f"Index findings: {len(idx_findings)}")
    for f in idx_findings:
        print(f"  [Index] {f['male_character']} + '{f['keyword']}': {f['line']}")

    # Save raw results
    test_report = {
        "total_shots": total_shots,
        "unique_shots": len(unique_shot_ids),
        "strictly_contiguous": results["shot_continuity"]["strictly_contiguous"],
        "scene_counts": results["shot_continuity"]["scene_counts"],
        "preamble_male_tears_candidates": len(preamble_findings),
        "shot_male_tears_candidates": len(shot_findings),
        "all_weeping_lines_count": len(all_weeping_occurrences),
        "failures": results["failures"]
    }
    
    with open(os.path.join(base_dir, 'tests', 'adversarial_results.json'), 'w', encoding='utf-8') as f:
        json.dump(test_report, f, indent=2, ensure_ascii=False)

    print("\nAdversarial test execution finished.")
    return len(results["failures"]) == 0

if __name__ == '__main__':
    success = run_adversarial_tests()
    sys.exit(0 if success else 1)
