#!/usr/bin/env python3
"""
Stage 12: Caption Generation

Uses Claude to generate captions from full context:
- Story understanding
- Original and adapted scripts
- Video prompts with all refinements

Commands:
- generate_captions: Generate captions for all scenes
- new_caption: Generate new caption for specific scene
- refine_caption: Refine caption with feedback
- select_caption: Select caption iteration
- status_captions: Show caption status for all scenes
"""

import sys
import json
import subprocess
from pathlib import Path
from datetime import datetime, timezone
from typing import Optional, Dict, List, Any

# Add parent to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from lib import (
    save_metadata,
    ensure_dir,
    claude_env,
    setup_generation_log,
)
from lib.path_utils_v3 import get_next_iteration
from lib.pipeline import load_pipeline, save_pipeline


# Global caption style
CAPTION_STYLE = {
    "font_size": 42,
    "font_color": "white",
    "bg_color": "black@0.7",
    "position": "bottom",
    "max_chars_per_line": 45,
    "box_border_width": 15
}


# ============================================================
# ASR (Automatic Speech Recognition) Functions
# ============================================================

def extract_audio_from_video(video_path: Path, output_path: Path = None) -> Optional[Path]:
    """
    Extract audio from video file using ffmpeg.

    Args:
        video_path: Path to video file
        output_path: Optional output path for audio. If None, uses video_path with .wav extension

    Returns:
        Path to extracted audio file, or None on failure
    """
    if output_path is None:
        output_path = video_path.with_suffix('.wav')

    try:
        result = subprocess.run([
            'ffmpeg', '-y',
            '-i', str(video_path),
            '-vn',  # No video
            '-acodec', 'pcm_s16le',  # WAV format
            '-ar', '16000',  # 16kHz sample rate (good for speech)
            '-ac', '1',  # Mono
            str(output_path)
        ], capture_output=True, text=True, timeout=60)

        if result.returncode == 0 and output_path.exists():
            return output_path
        return None

    except Exception as e:
        return None


def get_asr_transcription(video_path: Path, logger=None) -> Optional[dict]:
    """
    Transcribe audio from video using Whisper ASR.

    Args:
        video_path: Path to video file
        logger: Optional logger

    Returns:
        Dict with 'text', 'segments' (with start/end times), or None on failure
    """
    import os
    import tempfile

    # Get API key (try both names for compatibility)
    api_key = os.environ.get("LAOZHANG_KEY") or os.environ.get("LAOZHANG_API_KEY")
    if not api_key:
        if logger:
            logger.warning("LAOZHANG_KEY not set, skipping ASR")
        return None

    # Extract audio to temp file
    with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmp:
        audio_path = Path(tmp.name)

    try:
        audio_path = extract_audio_from_video(video_path, audio_path)
        if not audio_path:
            if logger:
                logger.warning(f"Failed to extract audio from {video_path}")
            return None

        if logger:
            logger.info(f"Extracted audio: {audio_path}")

        # Import the transcription function
        sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "scripts"))
        from laozhang_proxy import transcribe_audio_with_timestamps

        # Transcribe with timestamps
        result = transcribe_audio_with_timestamps(
            str(audio_path),
            api_key,
            model="whisper-1",
            language="en"  # Assume English for now
        )

        if logger:
            logger.info(f"ASR result: {len(result.get('segments', []))} segments, text: {result.get('text', '')[:100]}...")

        return result

    except Exception as e:
        if logger:
            logger.warning(f"ASR transcription failed: {e}")
        return None

    finally:
        # Cleanup temp file
        if audio_path and audio_path.exists():
            try:
                audio_path.unlink()
            except:
                pass


def get_session_id(take_path: Path) -> Optional[str]:
    """Get Claude session ID for the story."""
    session_file = take_path.parent / "session.txt"
    if session_file.exists():
        return session_file.read_text().strip()
    return None


def call_claude_for_captions(
    take_path: Path,
    scene: str,
    video_duration: float,
    context: dict,
    feedback: Optional[str] = None,
    asr_data: Optional[dict] = None,
    logger = None
) -> tuple[Optional[dict], Optional[str]]:
    """
    Call Claude to generate caption for a scene.

    Args:
        take_path: Path to take directory
        scene: Scene name (e.g., "02-scene")
        video_duration: Actual video duration in seconds
        context: Dict with story context (script, video_prompt, etc.)
        feedback: Optional refinement feedback
        asr_data: Optional ASR transcription with timestamps
        logger: Logger instance

    Returns:
        Tuple of (caption_data dict or None, prompt string or None)
    """
    session_id = get_session_id(take_path)
    if not session_id:
        if logger:
            logger.warning("No session file found")
        return None, None

    scene_num = int(scene.split('-')[0])

    # Build context for Claude
    # Check if scene has dialogue in the script
    scene_script = context.get('scene_script', {})
    scene_dialogue = scene_script.get("dialogue") or {}
    if isinstance(scene_dialogue, list):
        # Multiple speakers — combine caption texts
        has_script_dialogue = any(
            (d.get("text_caption") or d.get("text")) for d in scene_dialogue if isinstance(d, dict)
        )
    else:
        has_script_dialogue = bool(scene_dialogue.get("text_caption") or scene_dialogue.get("text"))

    prompt = f"""Generate a caption for Scene {scene_num} of our video.

VIDEO DURATION: {video_duration:.1f} seconds

SCENE FROM ADAPTED SCRIPT:
{json.dumps(scene_script, indent=2)}

VIDEO PROMPT (what we asked the AI to generate):
{context.get('video_prompt', 'Not available')}

"""

    # If script says no dialogue, tell Claude explicitly
    if not has_script_dialogue:
        prompt += """NOTE: This scene has NO DIALOGUE in the script. This is a music-only video.
Any speech detected by ASR below is likely a FALSE POSITIVE from AI-generated audio artifacts
(e.g. "Thank you for watching!", random words). IGNORE the ASR transcription entirely.
Return empty caption: {"text": "", "lines": [], "has_dialogue": false, "audio_cues": []}

"""

    # Add ASR data if available
    if asr_data and asr_data.get('segments'):
        prompt += f"""
=== ASR TRANSCRIPTION (what was ACTUALLY spoken in video) ===
Full text: "{asr_data.get('text', '')}"

Timestamped segments:
"""
        for seg in asr_data.get('segments', []):
            prompt += f"  [{seg['start']:.1f}s - {seg['end']:.1f}s]: \"{seg['text']}\"\n"

        prompt += """
IMPORTANT: Use ASR timestamps for caption timing! The ASR shows EXACTLY when speech occurs.

ASR vs SCRIPT TEXT - USE YOUR JUDGMENT:
- If ASR text is MEANINGFUL but different from script → use ASR (video actually said something different)
- If ASR text is GARBLED/NONSENSE/MISRECOGNIZED → use script's dialogue.text_caption as ground truth
- Examples of ASR errors to ignore: wrong words that don't make sense, incomplete phrases, gibberish
- Examples of valid ASR differences: rephrased sentences, ad-libs, different word order that still makes sense

The script's video_prompt and dialogue.text_caption show what we INTENDED. The ASR shows what was ACTUALLY said.
Use the one that makes more sense as a caption.
"""

    if feedback:
        prompt += f"""
REFINEMENT FEEDBACK:
{feedback}

Please adjust the caption based on this feedback.
"""

    prompt += """
CAPTION REQUIREMENTS:
1. Include ALL dialogue spoken in the scene (check video prompt for additional lines not in script)
2. For dialogue, use natural text with ellipsis (...) for pauses
3. Split long text into multiple lines (max 45 chars each)
4. Calculate timing based on video duration

AUDIO CUE RULES (IMPORTANT):
- ONLY use [audio cue] for sounds that CANNOT be seen on screen but are important for understanding
- Examples of GOOD audio cues: [gunshot], [phone ringing], [knock on door], [off-screen scream]
- DO NOT caption things that are visible: splashing water, dogs running, truck driving - these can be SEEN
- If everything in the scene is visible, return EMPTY lines array (no caption needed)
- Keep audio cues SHORT and simple: [gunshot] not [DOUBLE SHOTGUN BLAST]

OUTPUT FORMAT (JSON only, no markdown):
{
  "text": "Full caption text (all lines combined, empty string if no caption)",
  "lines": [
    {
      "text": "First line of caption",
      "start": 0.5,
      "end": 2.5,
      "position": "bottom"
    }
  ],
  "has_dialogue": true,
  "audio_cues": ["gunshot"]
}

For scenes with no dialogue AND no audio-only elements, return:
{
  "text": "",
  "lines": [],
  "has_dialogue": false,
  "audio_cues": []
}

IMPORTANT:
- For pauses in speech, use ellipsis: "Yeah... he can't swim."
- Include responses from other characters: if Dave asks and Paul replies, include BOTH
- Timing should leave ~0.5s at start and end of video
- Each line should be readable (1.5-3 seconds minimum)

Write ONLY the JSON, nothing else."""

    try:
        result = subprocess.run(
            ["claude", "--resume", session_id,
             "--model", "opus",
             "-p", prompt],
            capture_output=True,
            text=True,
            cwd=str(take_path.parent),
            timeout=120,
            env=claude_env()
        )

        if result.returncode != 0:
            if logger:
                error_detail = result.stderr or result.stdout
                logger.warning(f"Claude failed: {error_detail[:200] if error_detail else 'no output'}")
            return None

        # Parse JSON from response
        response = result.stdout.strip()

        # Try to extract JSON if wrapped in markdown
        if "```json" in response:
            response = response.split("```json")[1].split("```")[0].strip()
        elif "```" in response:
            response = response.split("```")[1].split("```")[0].strip()

        caption_data = json.loads(response)
        return caption_data, prompt

    except json.JSONDecodeError as e:
        if logger:
            logger.warning(f"Failed to parse Claude response as JSON: {e}")
            logger.warning(f"Response: {result.stdout[:500]}")
        return None, prompt
    except Exception as e:
        if logger:
            logger.warning(f"Claude error: {e}")
        return None, prompt


def get_scene_context(take_path: Path, scene: str, video_iteration: str) -> dict:
    """
    Gather full context for a scene.

    Returns dict with:
    - scene_script: Scene data from adapted script
    - video_prompt: Final video prompt used
    """
    scene_num = int(scene.split('-')[0])
    context = {}

    # Get scene from adapted script
    script_file = _get_current_script(take_path)
    if script_file and script_file.exists():
        with open(script_file) as f:
            script = json.load(f)
        for scene_data in script.get("scenes", []):
            if scene_data.get("number") == scene_num:
                context["scene_script"] = scene_data
                break

    # Get video prompt
    prompt_file = take_path / "10-videos" / scene / video_iteration / "prompt.txt"
    if prompt_file.exists():
        context["video_prompt"] = prompt_file.read_text()

    return context


def get_video_duration(take_path: Path, scene: str, iteration: str) -> float:
    """Get actual video duration from metadata."""
    video_meta_path = take_path / "10-videos" / scene / iteration / "metadata.json"

    if video_meta_path.exists():
        with open(video_meta_path) as f:
            meta = json.load(f)
        return float(meta.get("duration_actual", 5.0))

    return 5.0  # Default


def generate_caption_for_scene(
    take_path: Path,
    scene: str,
    video_iteration: str,
    feedback: Optional[str] = None,
    refined_from: Optional[str] = None,
    use_asr: bool = True,
    logger = None
) -> dict:
    """
    Generate caption for a single scene using Claude with optional ASR.

    Args:
        take_path: Path to take directory
        scene: Scene name (e.g., "02-scene")
        video_iteration: Video iteration this caption is for
        feedback: Optional feedback for refinement
        refined_from: Iteration being refined
        use_asr: Whether to use ASR for timing (default True)
        logger: Logger instance

    Returns:
        Result dict with status, iteration, caption data
    """
    scene_num = int(scene.split('-')[0])

    # Get video duration
    video_duration = get_video_duration(take_path, scene, video_iteration)

    if logger:
        logger.info(f"{scene}: Video duration {video_duration:.1f}s")

    # Get full context
    context = get_scene_context(take_path, scene, video_iteration)

    if not context.get("scene_script") and not context.get("video_prompt"):
        if logger:
            logger.warning(f"{scene}: No context available")
        return {
            "status": "error",
            "scene": scene,
            "error": "No script or video prompt found"
        }

    # Create caption directory and iteration path early (for saving debug artifacts)
    captions_dir = take_path / "12-captions" / scene
    ensure_dir(captions_dir)
    iteration = get_next_iteration(captions_dir)
    iter_path = captions_dir / iteration
    ensure_dir(iter_path)

    if logger:
        logger.info(f"{scene}: Creating caption iteration {iteration}")

    # Get ASR transcription for accurate timing
    asr_data = None
    if use_asr:
        video_path = take_path / "10-videos" / scene / video_iteration / "video.mp4"
        if video_path.exists():
            if logger:
                logger.info(f"{scene}: Running ASR transcription...")
            asr_data = get_asr_transcription(video_path, logger)
            if asr_data and asr_data.get('text'):
                if logger:
                    logger.info(f"{scene}: ASR detected: \"{asr_data.get('text', '')[:80]}...\"")
            else:
                if logger:
                    logger.info(f"{scene}: No speech detected by ASR")

            # Save ASR data to iteration directory
            if asr_data:
                asr_file = iter_path / "asr.json"
                with open(asr_file, 'w') as f:
                    json.dump(asr_data, f, indent=2)
                if logger:
                    logger.info(f"{scene}: ASR data saved to {iteration}/asr.json")

    # Call Claude to generate caption
    if logger:
        logger.info(f"{scene}: Calling Claude for caption generation")

    caption_data, claude_prompt = call_claude_for_captions(
        take_path, scene, video_duration, context, feedback, asr_data, logger
    )

    # Save Claude prompt to iteration directory
    if claude_prompt:
        prompt_file = iter_path / "claude_prompt.txt"
        prompt_file.write_text(claude_prompt)
        if logger:
            logger.info(f"{scene}: Claude prompt saved to {iteration}/claude_prompt.txt")

    if not caption_data:
        if logger:
            logger.warning(f"{scene}: Claude failed, using fallback")
        # Fallback to simple extraction
        caption_data = _fallback_caption(context, video_duration)

    if logger:
        logger.info(f"{scene}: {len(caption_data.get('lines', []))} line(s)")

    # Add metadata to caption data
    caption_data["scene"] = scene_num
    caption_data["scene_name"] = scene
    caption_data["video_duration"] = video_duration
    caption_data["style"] = CAPTION_STYLE.copy()

    # Save caption.json
    caption_file = iter_path / "caption.json"
    with open(caption_file, 'w') as f:
        json.dump(caption_data, f, indent=2)

    # Save feedback if provided
    if feedback:
        feedback_file = iter_path / "feedback.txt"
        feedback_file.write_text(feedback)

    # Save metadata
    metadata = {
        "stage": "12-captions",
        "scene": scene,
        "iteration": iteration,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "based_on": {
            "10-videos": video_iteration,
            "09-script-adapted": _get_script_iteration(take_path)
        },
        "refined_from": refined_from,
        "line_count": len(caption_data.get("lines", [])),
        "has_dialogue": caption_data.get("has_dialogue", False),
        "result": f"12-captions/{scene}/{iteration}/caption.json"
    }

    save_metadata(iter_path / "metadata.json", metadata)

    return {
        "status": "success",
        "scene": scene,
        "iteration": iteration,
        "lines": len(caption_data.get("lines", [])),
        "text": caption_data.get("text", ""),
        "has_dialogue": caption_data.get("has_dialogue", False),
        "caption_path": str(caption_file)
    }


def _fallback_caption(context: dict, video_duration: float) -> dict:
    """
    Fallback caption generation without Claude.

    Used when Claude is unavailable.
    """
    scene_script = context.get("scene_script", {})
    dialogue = scene_script.get("dialogue") or {}

    # Use text_caption for captions (with proper punctuation/ellipsis), fall back to text
    if isinstance(dialogue, list):
        texts = []
        for d in dialogue:
            if isinstance(d, dict):
                t = d.get("text_caption") or d.get("text")
                if t:
                    texts.append(t)
        text = " ".join(texts) if texts else None
    else:
        text = dialogue.get("text_caption") or dialogue.get("text")
    if text:
        return {
            "text": text,
            "lines": [{
                "text": text,
                "start": 0.5,
                "end": video_duration - 0.5,
                "position": "bottom"
            }],
            "has_dialogue": True,
            "audio_cues": []
        }
    else:
        # Check for audio cues
        audio = scene_script.get("audio_ambient", "")
        cues = []
        if "gunshot" in audio.lower() or "shotgun" in audio.lower():
            cues.append("gunshot")
        if "splash" in audio.lower():
            cues.append("splash")
        if "bark" in audio.lower():
            cues.append("dog barking")

        if cues:
            text = " ".join(f"[{c}]" for c in cues)
            return {
                "text": text,
                "lines": [{
                    "text": text,
                    "start": 0.5,
                    "end": min(2.0, video_duration - 0.5),
                    "position": "bottom"
                }],
                "has_dialogue": False,
                "audio_cues": cues
            }

    return {
        "text": "",
        "lines": [],
        "has_dialogue": False,
        "audio_cues": []
    }


def generate_captions(
    take_path: Path,
    force_regenerate: bool = False
) -> dict:
    """
    Generate captions for all scenes using Claude.

    Args:
        take_path: Path to take directory
        force_regenerate: If True, regenerate all captions

    Returns:
        Result dict with generated/skipped scenes
    """
    logger = setup_generation_log(take_path)
    logger.section("Stage 12: Generate Captions")

    # Load pipeline
    pipeline = load_pipeline(take_path)
    video_selections = pipeline.get("10-videos", {})
    caption_selections = pipeline.get("12-captions", {})

    if not isinstance(caption_selections, dict):
        caption_selections = {}

    if not isinstance(video_selections, dict):
        return {
            "status": "error",
            "error": "No videos selected. Complete Stage 10 first."
        }

    # Load adapted script to get scene count
    script_file = _get_current_script(take_path)
    if not script_file:
        return {
            "status": "error",
            "error": "Adapted script not found. Complete Stage 08 first."
        }

    with open(script_file) as f:
        script = json.load(f)

    results = {
        "generated": [],
        "skipped": [],
        "already_exists": [],
        "no_video": [],
        "errors": []
    }

    # Process each scene
    scene_count = len(script.get("scenes", []))

    for scene_num in range(1, scene_count + 1):
        scene = f"{scene_num:02d}-scene"

        # Check if video is selected
        video_iter = video_selections.get(scene)
        if not video_iter:
            results["no_video"].append(scene)
            logger.info(f"{scene}: No video selected - skipping")
            continue

        # Check if caption already exists (and matches video)
        existing_caption = caption_selections.get(scene)
        if existing_caption and not force_regenerate:
            # Check if existing caption is for same video
            caption_meta_path = take_path / "12-captions" / scene / existing_caption / "metadata.json"
            if caption_meta_path.exists():
                with open(caption_meta_path) as f:
                    meta = json.load(f)
                if meta.get("based_on", {}).get("10-videos") == video_iter:
                    results["already_exists"].append({
                        "scene": scene,
                        "iteration": existing_caption
                    })
                    logger.info(f"{scene}: Caption {existing_caption} already exists for video {video_iter}")
                    continue

        # Generate caption
        result = generate_caption_for_scene(
            take_path, scene, video_iter, logger=logger
        )

        if result["status"] == "success":
            results["generated"].append(result)
            # Auto-select the generated caption
            caption_selections[scene] = result["iteration"]
        elif result["status"] == "skipped":
            results["skipped"].append(scene)
            caption_selections[scene] = None
        else:
            results["errors"].append({
                "scene": scene,
                "error": result.get("error", "Unknown error")
            })

    # Save updated pipeline
    pipeline["12-captions"] = caption_selections
    save_pipeline(take_path, pipeline)

    logger.info(f"Generated: {len(results['generated'])} captions")
    logger.info(f"Already exists: {len(results['already_exists'])}")
    logger.info(f"Errors: {len(results['errors'])}")

    return {
        "status": "success",
        "results": results
    }


def new_caption(
    take_path: Path,
    scene: str,
    feedback: Optional[str] = None
) -> dict:
    """
    Generate new caption for a specific scene.

    Args:
        take_path: Path to take directory
        scene: Scene name
        feedback: Optional feedback to guide generation

    Returns:
        Result dict
    """
    logger = setup_generation_log(take_path)
    logger.section(f"Stage 12: New Caption - {scene}")

    pipeline = load_pipeline(take_path)
    video_selections = pipeline.get("10-videos", {})

    video_iter = video_selections.get(scene)
    if not video_iter:
        return {
            "status": "error",
            "error": f"No video selected for {scene}"
        }

    result = generate_caption_for_scene(
        take_path, scene, video_iter,
        feedback=feedback, logger=logger
    )

    if result["status"] == "success":
        # Update pipeline
        if "12-captions" not in pipeline or not isinstance(pipeline["12-captions"], dict):
            pipeline["12-captions"] = {}
        pipeline["12-captions"][scene] = result["iteration"]
        save_pipeline(take_path, pipeline)

    return result


def refine_caption(
    take_path: Path,
    scene: str,
    iteration: str,
    feedback: str
) -> dict:
    """
    Refine existing caption with feedback using Claude.

    Args:
        take_path: Path to take directory
        scene: Scene name
        iteration: Base iteration to refine
        feedback: Refinement feedback

    Returns:
        Result dict with new iteration
    """
    logger = setup_generation_log(take_path)
    logger.section(f"Stage 12: Refine Caption - {scene}/{iteration}")

    # Load base caption
    base_path = take_path / "12-captions" / scene / iteration / "caption.json"
    if not base_path.exists():
        return {
            "status": "error",
            "error": f"Caption iteration {iteration} not found"
        }

    # Load base metadata for video iteration
    base_meta_path = take_path / "12-captions" / scene / iteration / "metadata.json"
    with open(base_meta_path) as f:
        base_meta = json.load(f)

    video_iter = base_meta.get("based_on", {}).get("10-videos", "001")

    # Generate refined caption
    result = generate_caption_for_scene(
        take_path, scene, video_iter,
        feedback=feedback,
        refined_from=iteration,
        logger=logger
    )

    if result["status"] == "success":
        # Update pipeline
        pipeline = load_pipeline(take_path)
        if "12-captions" not in pipeline or not isinstance(pipeline["12-captions"], dict):
            pipeline["12-captions"] = {}
        pipeline["12-captions"][scene] = result["iteration"]
        save_pipeline(take_path, pipeline)

    return result


def select_caption(take_path: Path, scene: str, iteration: str) -> dict:
    """
    Select caption iteration for a scene.

    Args:
        take_path: Path to take directory
        scene: Scene name
        iteration: Iteration to select

    Returns:
        Result dict
    """
    logger = setup_generation_log(take_path)
    logger.section(f"Stage 12: Select Caption - {scene}/{iteration}")

    # Validate iteration exists
    caption_path = take_path / "12-captions" / scene / iteration / "caption.json"
    if not caption_path.exists():
        return {
            "status": "error",
            "error": f"Caption iteration {iteration} not found"
        }

    # Update pipeline
    pipeline = load_pipeline(take_path)
    if "12-captions" not in pipeline or not isinstance(pipeline["12-captions"], dict):
        pipeline["12-captions"] = {}

    pipeline["12-captions"][scene] = iteration
    save_pipeline(take_path, pipeline)

    logger.info(f"Selected {iteration} for {scene}")

    return {
        "status": "success",
        "scene": scene,
        "iteration": iteration
    }


def status_captions(take_path: Path) -> dict:
    """
    Get caption status for all scenes.

    Returns:
        Dict with scene statuses
    """
    pipeline = load_pipeline(take_path)
    video_selections = pipeline.get("10-videos", {})
    caption_selections = pipeline.get("12-captions", {})

    if not isinstance(caption_selections, dict):
        caption_selections = {}

    # Load script to know total scenes
    script_file = _get_current_script(take_path)
    if not script_file:
        return {"status": "error", "error": "Script not found"}

    with open(script_file) as f:
        script = json.load(f)

    scenes = []
    for scene_data in script.get("scenes", []):
        scene_num = scene_data["number"]
        scene = f"{scene_num:02d}-scene"

        dialogue = scene_data.get("dialogue") or {}
        if isinstance(dialogue, list):
            has_dialogue = any(
                (d.get("text_caption") or d.get("text")) for d in dialogue if isinstance(d, dict)
            )
        else:
            has_dialogue = bool(dialogue.get("text_caption") or dialogue.get("text"))
        video_iter = video_selections.get(scene) if isinstance(video_selections, dict) else None
        caption_iter = caption_selections.get(scene)

        # Get caption text if selected
        caption_text = None
        if caption_iter:
            caption_file = take_path / "12-captions" / scene / caption_iter / "caption.json"
            if caption_file.exists():
                with open(caption_file) as f:
                    caption_data = json.load(f)
                caption_text = caption_data.get("text", "")

        # Count iterations
        caption_dir = take_path / "12-captions" / scene
        iterations = []
        if caption_dir.exists():
            for item in sorted(caption_dir.iterdir()):
                if item.is_dir() and item.name.isdigit():
                    iterations.append(item.name)

        scenes.append({
            "scene": scene,
            "has_dialogue": has_dialogue,
            "video_selected": video_iter,
            "caption_selected": caption_iter,
            "caption_text": caption_text,
            "iterations": iterations,
            "iteration_count": len(iterations)
        })

    return {
        "status": "success",
        "scenes": scenes
    }


# Helper functions

def _get_current_script(take_path: Path) -> Optional[Path]:
    """Get current adapted script path."""
    # Check pipeline for selected iteration
    pipeline = load_pipeline(take_path)
    script_iter = pipeline.get("09-script-adapted")

    if script_iter:
        script_file = take_path / "09-script-adapted" / script_iter / "script.json"
        if script_file.exists():
            return script_file

    # Fallback: find most recent
    script_stage = take_path / "09-script-adapted"
    if not script_stage.exists():
        return None

    iterations = []
    for item in script_stage.iterdir():
        if item.is_dir() and item.name.isdigit():
            script_file = item / "script.json"
            if script_file.exists():
                iterations.append(item.name)

    if iterations:
        latest = max(iterations)
        return script_stage / latest / "script.json"

    return None


def _get_script_iteration(take_path: Path) -> str:
    """Get current script iteration number."""
    script_file = _get_current_script(take_path)
    if script_file:
        return script_file.parent.name
    return "unknown"
