#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Prompt Compiler for Tran_Empire AI Cinema Production
Extracts 10-second shots from screenplay markdown files and compiles them into
ready-to-use AI generation prompts (Keyframes & Motion prompts).
"""

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

def load_json(file_path):
    if os.path.exists(file_path):
        with open(file_path, "r", encoding="utf-8") as f:
            return json.load(f)
    return {}

def compile_screenplay_prompts(screenplay_path, rules_path, characters_path, output_json_path=None):
    screenplay_file = Path(screenplay_path)
    if not screenplay_file.exists():
        print(f"Error: Screenplay file {screenplay_path} not found.")
        return []

    rules = load_json(rules_path)
    characters = load_json(characters_path).get("characters", {})

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

    # Pattern to capture shot blocks with dynamic duration (2s - 10s)
    # e.g.: ##### Shot 01: `ep01_scene01_shot01` (10s) or (3s - REACTION CUT)
    shot_pattern = re.compile(
        r"#####\s+Shot\s+(\d+):\s*`([^`]+)`(?:\s*\(([^)]*)\))?\s*\n"
        r"(.*?)(?=(?:#####\s+Shot|\n####\s+CẢNH|\n##\s+HỒI|\Z))",
        re.DOTALL | re.IGNORECASE
    )

    shots_compiled = []
    style_suffix = ", ".join(rules.get("master_style_tokens", [
        "cinematic historical epic",
        "13th century Dai Viet Tran Dynasty aesthetic",
        "authentic Vietnamese archaeological fidelity",
        "chiaroscuro lighting",
        "8k resolution master shot"
    ]))

    negative_prompt = ", ".join(rules.get("negative_prompts", []))

    for match in shot_pattern.finditer(text):
        shot_num = match.group(1)
        shot_id = match.group(2)
        paren_text = match.group(3) or ""
        shot_duration = 10
        if paren_text:
            dur_m = re.search(r"(\d+)\s*(?:s|giây)", paren_text, re.IGNORECASE)
            if dur_m:
                shot_duration = int(dur_m.group(1))
        shot_body = match.group(4).strip()

        # Extract Visual description (FlowKit: Bối cảnh & Ánh sáng or Thị giác)
        visual_match = re.search(r"-\s*\*\*(?:Bối cảnh & Ánh sáng|Thị giác)\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        visual_desc = visual_match.group(1).strip() if visual_match else ""

        # Extract Camera framing (FlowKit: Góc máy & Tiêu cự or Cỡ cảnh & Máy quay)
        camera_match = re.search(r"-\s*\*\*(?:Góc máy & Tiêu cự|Góc máy|Cỡ cảnh & Máy quay)\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        camera_desc = camera_match.group(1).strip() if camera_match else "Cinematic Medium Shot"

        # Extract Camera Motion (FlowKit: Chuyển động Camera)
        motion_match = re.search(r"-\s*\*\*Chuyển động Camera\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        cam_motion_desc = motion_match.group(1).strip() if motion_match else "continuous smooth cinematic motion"

        # Extract Character & Performance (FlowKit: Nhân vật & Diễn xuất)
        perf_match = re.search(r"-\s*\*\*Nhân vật & Diễn xuất.*?\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        perf_desc = perf_match.group(1).strip() if perf_match else ""

        # Extract Vocal / Dialogue / Lip Guard
        vocal_match = re.search(r"-\s*\*\*Thoại & Khẩu hình\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        if not vocal_match:
            vocal_match = re.search(r"\*Stem 4 \(Vocal\)\*:\s*(.*?)(?=\n\s*-|\Z)", shot_body, re.DOTALL)
        vocal_desc = vocal_match.group(1).strip() if vocal_match else "None"

        # Extract First Frame and Last Frame
        first_frame_m = re.search(r"-\s*\*\*First Frame\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        first_frame_desc = first_frame_m.group(1).strip() if first_frame_m else ""

        last_frame_m = re.search(r"-\s*\*\*Last Frame\*\*:\s*(.*?)(?=\n-\s*\*\*|\Z)", shot_body, re.DOTALL)
        last_frame_desc = last_frame_m.group(1).strip() if last_frame_m else ""

        # Build Midjourney / Imagen 3 Keyframe Prompt
        keyframe_prompt = f"{camera_desc}, {visual_desc}, {style_suffix} --ar 16:9"

        # Build Video Motion Prompt (Wan2.1 / LTX-Video / Muse / Kling)
        # AUDIO GUARD DIRECTIVE: Strictly NO BGM in shot prompts to prevent audio desynchronization across cuts.
        # BGM is exclusively mixed in post-production on DaVinci Resolve.
        action_summary = f"{perf_desc}; {visual_desc}" if perf_desc else visual_desc
        motion_prompt = (
            f"{camera_desc}, {cam_motion_desc}, {action_summary}, "
            f"realistic water and fabric physics, high production value {shot_duration}s shot. "
            f"[AUDIO GUARD: No background music, no soundtrack, ambient environment and foley only]"
        )

        audio_negative_tokens = "background music, musical soundtrack, singing, orchestral music, disco, electronic beat, jarring music cuts"
        final_negative = f"{negative_prompt}, {audio_negative_tokens}" if negative_prompt else audio_negative_tokens

        shot_item = {
            "shot_id": shot_id,
            "shot_number": int(shot_num),
            "camera_framing": camera_desc,
            "camera_motion": cam_motion_desc,
            "visual_action": visual_desc,
            "character_performance": perf_desc,
            "dialogue_vocal": vocal_desc,
            "first_frame": first_frame_desc,
            "last_frame": last_frame_desc,
            "keyframe_ai_prompt": keyframe_prompt,
            "motion_video_prompt": motion_prompt,
            "audio_guard": "STRICT_NO_BGM_POST_PRODUCTION_ONLY",
            "negative_prompt": final_negative,
            "duration_sec": shot_duration
        }
        shots_compiled.append(shot_item)

    if output_json_path:
        out_p = Path(output_json_path)
        out_p.parent.mkdir(parents=True, exist_ok=True)
        with open(out_p, "w", encoding="utf-8") as f:
            json.dump({
                "screenplay": screenplay_file.name,
                "total_shots": len(shots_compiled),
                "total_duration_sec": sum(s["duration_sec"] for s in shots_compiled),
                "shots": shots_compiled
            }, f, ensure_ascii=False, indent=2)
        print(f"Compiled {len(shots_compiled)} shots to {output_json_path}")

    return shots_compiled

def main():
    parser = argparse.ArgumentParser(description="Compile screenplay to AI generation prompts")
    parser.add_argument("screenplay", help="Path to screenplay markdown file")
    parser.add_argument("--rules", default=None, help="Path to master prompt rules")
    parser.add_argument("--chars", default=None, help="Path to character prompts")
    parser.add_argument("--output", help="Output JSON path")
    args = parser.parse_args()

    rules_path = args.rules
    if not rules_path:
        for candidate in ["02_AI_Prompts/master_prompt_rules.json", "Tran_Empire/02_AI_Prompts/master_prompt_rules.json"]:
            if os.path.exists(candidate):
                rules_path = candidate
                break

    chars_path = args.chars
    if not chars_path:
        for candidate in ["02_AI_Prompts/characters_consistent_prompts.json", "Tran_Empire/02_AI_Prompts/characters_consistent_prompts.json"]:
            if os.path.exists(candidate):
                chars_path = candidate
                break

    compile_screenplay_prompts(args.screenplay, rules_path, chars_path, args.output)

if __name__ == "__main__":
    main()
