#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Screenplay & Shot Validator for Tran_Empire Cinematic Epic
Verifies:
- 3-Act Structure (Hồi 1, Hồi 2, Hồi 3)
- Scene counts and 10s atomic shot IDs (epXX_sceneYY_shotZZ)
- 4-Stem Audio presence (Stem 1: BGM, Stem 2: Ambience, Stem 3: Foley, Stem 4: Vocal)
- Duration estimation (~30 minutes / 1800 seconds)
"""

import os
import re
import sys
import json
import argparse
from pathlib import Path

def validate_screenplay(file_path, min_runtime_sec=0, check_flowkit=False):
    path = Path(file_path)
    if not path.exists():
        return {"file": str(file_path), "valid": False, "error": "File does not exist"}

    with open(path, "r", encoding="utf-8") as f:
        content = f.read()

    lines = content.splitlines()
    total_lines = len(lines)

    # 1. Check Acts
    acts_found = []
    for act_num in [1, 2, 3]:
        act_pattern = rf"(HỒI|Hồi|ACT|Act)\s*{act_num}[:\s]"
        if re.search(act_pattern, content):
            acts_found.append(act_num)

    # 2. Check Scenes
    scene_matches = re.findall(r"(CẢNH|Cảnh|SCENE|Scene)\s*(\d+)[:\s]", content)
    unique_scenes = sorted(list(set(int(m[1]) for m in scene_matches)))

    # 3. Check Shots and Dynamic Durations (2s - 10s Directorial Standard)
    # Pattern: ##### Shot 01: `ep01_scene01_shot01` (10s) or (3s - REACTION CUT)
    shot_matches = re.findall(
        r"#####\s+Shot\s+(\d+):\s*`([^`]+)`(?:\s*\(([^)]*)\))?",
        content,
        re.IGNORECASE
    )

    durations = []
    shot_ids = []
    invalid_duration_shots = []

    if shot_matches:
        for s_num, s_id, s_paren in shot_matches:
            shot_ids.append(s_id)
            sec = 10
            if s_paren:
                dur_m = re.search(r"(\d+)\s*(?:s|giây)", s_paren, re.IGNORECASE)
                if dur_m:
                    sec = int(dur_m.group(1))
            durations.append(sec)
            if sec < 2 or sec > 10:
                invalid_duration_shots.append(f"{s_id} ({sec}s)")
    else:
        # Fallback regex
        found_ids = re.findall(r"ep\d+_scene\d+_shot\d+", content)
        if not found_ids:
            found_ids = re.findall(r"(?:Shot|shot)\s*(\d+)[:\s]", content)
        shot_ids = found_ids
        durations = [10] * len(shot_ids)

    total_shots = len(shot_ids)
    total_duration_sec = sum(durations)
    estimated_duration_min = round(total_duration_sec / 60, 2)
    avg_duration = round(total_duration_sec / total_shots, 1) if total_shots > 0 else 0

    # 4. Check Audio Stems
    stem1_bgm = len(re.findall(r"(Stem 1|BGM|Nhạc nền)", content, re.IGNORECASE))
    stem2_ambience = len(re.findall(r"(Stem 2|Ambience|Môi trường)", content, re.IGNORECASE))
    stem3_foley = len(re.findall(r"(Stem 3|Foley|Tiếng động)", content, re.IGNORECASE))
    stem4_vocal = len(re.findall(r"(Stem 4|Vocal|Thoại|Lời thoại)", content, re.IGNORECASE))

    has_4stems = (stem1_bgm > 0 and stem2_ambience > 0 and stem3_foley > 0 and stem4_vocal > 0)

    # 5. Check FlowKit Criteria
    first_frames = len(re.findall(r"(First Frame|Start Frame|Frame 001|Đầu cú máy)", content, re.IGNORECASE))
    last_frames = len(re.findall(r"(Last Frame|Tail Frame|Frame cuối|Kết cú máy)", content, re.IGNORECASE))
    camera_motions = len(re.findall(r"(Chuyển động Camera|Camera Motion|Chuyển động máy)", content, re.IGNORECASE))
    camera_angles = len(re.findall(r"(Góc máy|Camera Angle|Cỡ cảnh)", content, re.IGNORECASE))
    characters = len(re.findall(r"(Nhân vật|Character)", content, re.IGNORECASE))
    dialogues = len(re.findall(r"(Thoại|Lời thoại|Dialogue|Vocal|Closed Lips)", content, re.IGNORECASE))

    flowkit_compliant = (
        first_frames >= max(1, int(total_shots * 0.8)) and
        last_frames >= max(1, int(total_shots * 0.8)) and
        camera_motions >= max(1, int(total_shots * 0.8))
    )

    # Validation verdict
    is_valid = True
    issues = []

    if len(acts_found) < 3:
        is_valid = False
        issues.append(f"Missing acts. Found: {acts_found}, expected [1, 2, 3]")

    if total_shots < 3:
        is_valid = False
        issues.append(f"Critically insufficient shots: {total_shots} (Target: ~180 shots for 30m episode)")
    elif total_shots < 30:
        issues.append(f"Pilot core draft: {total_shots} shots ({total_duration_sec}s, Target: ~180 shots for 30m episode)")

    if invalid_duration_shots:
        is_valid = False
        issues.append(f"Directorial duration violation (<2s or >10s): {', '.join(invalid_duration_shots[:5])}")

    if not has_4stems:
        issues.append(f"Missing complete 4 stems. Stem1: {stem1_bgm}, Stem2: {stem2_ambience}, Stem3: {stem3_foley}, Stem4: {stem4_vocal}")

    if min_runtime_sec > 0 and total_duration_sec < min_runtime_sec:
        is_valid = False
        issues.append(f"Runtime deficit: {total_duration_sec}s / required {min_runtime_sec}s (Deficit: {min_runtime_sec - total_duration_sec}s)")

    if check_flowkit and not flowkit_compliant:
        is_valid = False
        issues.append(f"FlowKit missing fields: FirstFrame={first_frames}/{total_shots}, LastFrame={last_frames}/{total_shots}, CameraMotion={camera_motions}/{total_shots}")

    report = {
        "file": str(path.name),
        "total_lines": total_lines,
        "acts_detected": acts_found,
        "scenes_count": len(unique_scenes),
        "total_shots": total_shots,
        "total_duration_sec": total_duration_sec,
        "estimated_duration_minutes": estimated_duration_min,
        "avg_duration_sec": avg_duration,
        "stems_presence": {
            "stem1_bgm": stem1_bgm,
            "stem2_ambience": stem2_ambience,
            "stem3_foley": stem3_foley,
            "stem4_vocal": stem4_vocal,
            "all_4_present": has_4stems
        },
        "flowkit_stats": {
            "first_frames": first_frames,
            "last_frames": last_frames,
            "camera_motions": camera_motions,
            "camera_angles": camera_angles,
            "characters": characters,
            "dialogues": dialogues,
            "flowkit_compliant": flowkit_compliant
        },
        "valid": is_valid,
        "warnings_or_errors": issues
    }
    return report

def main():
    parser = argparse.ArgumentParser(description="Validate Tran Empire Screenplay files")
    parser.add_argument("path", help="Path to screenplay file or directory containing screenplays")
    parser.add_argument("--min-runtime", type=int, default=0, help="Minimum required runtime in seconds (e.g. 1800 for 30 min)")
    parser.add_argument("--flowkit", action="store_true", help="Enforce strict FlowKit criteria (First Frame, Last Frame, Camera Motion)")
    args = parser.parse_args()

    target = Path(args.path)
    if target.is_file():
        files = [target]
    elif target.is_dir():
        files = list(target.rglob("*.md"))
    else:
        print(f"Error: {target} not found")
        sys.exit(1)

    print(f"=== TRAN EMPIRE SCREENPLAY VALIDATOR (FLOWKIT & RUNTIME EDITION) ===")
    if args.min_runtime > 0:
        print(f"Enforcing Minimum Runtime: {args.min_runtime}s (~{args.min_runtime / 60:.1f} min)")
    if args.flowkit:
        print(f"Enforcing Strict FlowKit Standards (First Frame, Last Frame, Camera Motion, 4 Stems)")
    print(f"Checking {len(files)} file(s)...\n")

    overall_valid = True
    for f in files:
        if "Bible" in str(f) or "Guides" in str(f) or "Prompts" in str(f):
            continue
        res = validate_screenplay(f, min_runtime_sec=args.min_runtime, check_flowkit=args.flowkit)
        status = "PASSED [OK]" if res["valid"] else "FAILED [X]"
        print(f"[{status}] {res['file']}")
        print(f"  - Acts: {res['acts_detected']} | Scenes: {res['scenes_count']} | Shots: {res['total_shots']} (Avg: {res['avg_duration_sec']}s)")
        print(f"  - Runtime: {res['total_duration_sec']}s (~{res['estimated_duration_minutes']} min) | Stems: {res['stems_presence']['all_4_present']}")
        fk = res['flowkit_stats']
        print(f"  - FlowKit Specs: FirstFrame={fk['first_frames']} | LastFrame={fk['last_frames']} | CamMotion={fk['camera_motions']} | Compliant: {fk['flowkit_compliant']}")
        if res["warnings_or_errors"]:
            for issue in res["warnings_or_errors"]:
                print(f"    ! {issue}")
        print()
        if not res["valid"]:
            overall_valid = False

    if overall_valid:
        print("ALL CHECKED SCREENPLAYS MEET ARCHITECTURAL CRITERIA.")
    else:
        print("SOME SCREENPLAYS HAVE ISSUES TO RESOLVE.")
        sys.exit(1)

if __name__ == "__main__":
    main()
