#!/usr/bin/env python3
"""
Stage 1: Story Generation

For template-based projects (with brief.md + person photos), generates story.md
by calling Claude to analyze the brief, reference photos, and template guidelines.

Backwards compatible: if story.md already exists (manual placement), auto-selects
it and returns success without calling Claude.
"""

import json
import subprocess
import sys
from pathlib import Path
from datetime import datetime, timezone

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

from lib import (
    save_metadata,
    ensure_dir,
    setup_generation_log,
    log_event,
    claude_env
)
from lib.path_utils_v3 import get_next_iteration
from lib.feedback_history_v5 import get_feedback_history_for_claude
import reference_loader


def _find_existing_story(take_path: Path) -> Path | None:
    """Find existing story.md in any 01-story iteration."""
    story_dir = take_path / "01-story"
    if not story_dir.exists():
        return None

    for d in sorted(story_dir.iterdir()):
        if d.is_dir() and d.name.isdigit():
            candidate = d / "story.md"
            if candidate.exists():
                return candidate
    return None


def execute(take_path: Path, story_name: str, inputs: dict,
            feedback: str = None, refined_from: str = None) -> dict:
    """
    Execute Stage 1: Story generation.

    If story.md already exists in 01-story/, auto-selects that iteration
    and returns success (backwards compatibility).

    Otherwise, generates story from brief + person photos + template context.

    Args:
        take_path: Path to take directory
        story_name: Story name (slug)
        inputs: Parent stage iterations (empty for stage 1)
        feedback: User feedback text (if any)
        refined_from: Iteration being refined (if refinement)
    """
    logger = setup_generation_log(take_path)
    logger.section("Stage 1: Story Generation")

    # Check for existing story.md (backwards compatibility)
    existing_story = _find_existing_story(take_path)
    if existing_story and not feedback and not refined_from:
        # Auto-select existing story
        iteration = existing_story.parent.name
        logger.info(f"Found existing story: {existing_story} (auto-select)")

        # Ensure metadata exists
        iter_path = existing_story.parent
        meta_file = iter_path / "metadata.json"
        if not meta_file.exists():
            save_metadata(meta_file, {
                "stage": "01-story",
                "iteration": iteration,
                "created_at": datetime.now(timezone.utc).isoformat(),
                "based_on": {},
                "refined_from": None,
                "rating": None,
                "result": f"01-story/{iteration}/story.md",
                "source": "manual",
            })

        print(f"Found existing story: 01-story/{iteration}/story.md")
        return {
            "status": "success",
            "iteration": iteration,
            "result": f"01-story/{iteration}/story.md",
            "user_message": "Existing story found and auto-selected.",
        }

    # No existing story — generate from brief + photos + template
    project_path = take_path.parent
    refs = reference_loader.load_project_references(project_path)

    # Episode brief — specific concept for this episode
    episode_brief = ""
    episode_brief_file = take_path / "brief.txt"
    if episode_brief_file.exists():
        episode_brief = episode_brief_file.read_text().strip()
        if episode_brief:
            logger.info(f"Episode brief: {episode_brief[:100]}...")

    # Detect literary adaptation mode (brief from story_pipeline S6)
    is_literary_adaptation = _is_literary_adaptation(episode_brief)
    if is_literary_adaptation:
        logger.info("Literary adaptation mode detected (story pipeline brief)")

    if not refs.get("available") and not is_literary_adaptation:
        return {
            "status": "error",
            "error": "No references found. Either place story.md manually in 01-story/001/ "
                     "or set up references/ with brief.md and person photos."
        }

    brief = refs.get("brief", "") if refs.get("available") else ""

    if not brief and not episode_brief:
        return {
            "status": "error",
            "error": "No brief found. Add brief.md to project root or brief.txt to episode."
        }

    # Collect person info
    person_description = ""
    person_photos = []
    for person_name, person_data in refs.get("people", {}).items():
        if person_data.get("description"):
            person_description += f"### {person_name.replace('_', ' ').title()}\n"
            person_description += person_data["description"] + "\n\n"
        for photo in person_data.get("photos", []):
            person_photos.append(photo)

    if not person_description:
        person_description = "(No person description provided)"

    # Collect ALL reference photos (person + location + style)
    all_photos = []
    if person_photos:
        all_photos.append("**Person photos** (may include face shots, lifestyle shots, mood/style references):")
        for p in person_photos:
            all_photos.append(f"- `{p}`")
    location_photos = refs.get("locations", [])
    if location_photos:
        all_photos.append("\n**Location references:**")
        for p in location_photos:
            all_photos.append(f"- `{p}`")
    style_photos = refs.get("style", [])
    if style_photos:
        all_photos.append("\n**Style/mood references:**")
        for p in style_photos:
            all_photos.append(f"- `{p}`")

    if all_photos:
        photo_section = "\n".join(all_photos)
    else:
        photo_section = "(No reference photos available)"

    # Determine iteration number
    stage_dir = take_path / "01-story"
    ensure_dir(stage_dir)
    iteration = get_next_iteration(stage_dir)

    iter_path = stage_dir / iteration
    ensure_dir(iter_path)

    logger.info(f"Creating iteration {iteration}")
    if refined_from:
        logger.info(f"Refined from: {refined_from}")

    # Save feedback if provided
    if feedback:
        (iter_path / "feedback.txt").write_text(feedback)
        logger.info("Saved feedback.txt")

    # Load session ID
    session_file = take_path.parent / "session.txt"
    if not session_file.exists():
        return {"status": "error", "error": "Session file not found"}

    session_id = session_file.read_text().strip()
    logger.info(f"Session ID: {session_id}")

    # Feedback history from refinement chain
    feedback_history = get_feedback_history_for_claude(
        take_path, "01-story", refined_from
    )

    # Build prompt
    prompt_template_path = Path(__file__).parent.parent.parent / "prompts" / "stage_story.txt"

    if prompt_template_path.exists():
        prompt = prompt_template_path.read_text()
        prompt = prompt.replace("{brief}", brief or "(No project brief provided)")
        prompt = prompt.replace("{episode_brief}", episode_brief or "(No specific episode concept — use project brief)")
        prompt = prompt.replace("{person_description}", person_description)
        prompt = prompt.replace("{photo_paths}", photo_section)
    else:
        prompt = f"""# Stage 1: Story Generation

You are creating a story concept for a short-form video (30-60 seconds).

## Project Brief
{brief}

## Episode Concept
{episode_brief or "(No specific episode concept)"}

## Person Description
{person_description}

## Person Photos
{photo_section}

## Task

Read the reference photos and create a story concept.

Write TWO files:
1. story.md — Visual story concept with scenes, hook, character notes
2. user_message.txt — Friendly summary of your creative vision
"""

    # Add template/references context if project has them
    template_context = reference_loader.get_full_prompt_context(project_path)
    if template_context:
        prompt += "\n\n" + template_context
        logger.info(f"Added template context ({len(template_context)} chars)")

    # Inject creative_notes and scene_count from story_pipeline.json
    story_pipeline_file = project_path / "story_pipeline.json"
    if story_pipeline_file.exists():
        try:
            sp_data = json.loads(story_pipeline_file.read_text())
            creative_notes = sp_data.get("creative_notes")
            scene_count = sp_data.get("scene_count")
            if creative_notes:
                prompt += (f"\n\n## CREATIVE DIRECTION\n\n"
                           f"Channel-level creative requirements:\n\n{creative_notes}\n")
                logger.info(f"Added creative_notes ({len(creative_notes)} chars)")
            if scene_count:
                prompt += (f"\n\n## SCENE COUNT\n\n"
                           f"Generate exactly **{scene_count} scenes** for this episode.\n")
                logger.info(f"Scene count: {scene_count}")
        except (json.JSONDecodeError, OSError):
            pass

    # Add feedback
    if feedback_history:
        prompt += f"\n\n## Previous Feedback\n\n{feedback_history}\n"
    elif feedback:
        prompt += f"\n\n## User Feedback\n\n{feedback}\n"

    # Prepend output directory
    story_path = take_path.parent
    relative_iter_path = iter_path.relative_to(story_path)
    prompt = f"## Output Directory\n\nWrite ALL output files to: {relative_iter_path}/\n\n{prompt}"

    # Save prompt
    (iter_path / "prompt.txt").write_text(prompt)
    logger.info("Saved prompt")

    # Call Claude
    logger.info("Calling Claude...")
    print("\n" + "=" * 60)
    print(f"Stage 1: Story Generation (iteration {iteration})")
    print(f"Session: {session_id}")
    print("=" * 60)

    try:
        result = subprocess.run(
            ["claude", "--resume", session_id,
             "--allowedTools", "Read,Write,Glob,Grep",
             "-p", prompt],
            capture_output=True, text=True,
            cwd=str(story_path), timeout=600,
            env=claude_env()
        )

        if result.returncode != 0:
            error_detail = result.stderr or result.stdout
            error_msg = f"Claude returned code {result.returncode}: {error_detail}"
            logger.error(error_msg)
            return {"status": "error", "error": error_msg}

        logger.info("Claude completed successfully")

    except subprocess.TimeoutExpired:
        return {"status": "error", "error": "Claude timed out after 10 minutes"}
    except Exception as e:
        return {"status": "error", "error": f"Exception calling Claude: {str(e)}"}

    # Validate output
    story_file = iter_path / "story.md"
    if not story_file.exists():
        return {"status": "error", "error": "Expected output: story.md not created"}

    logger.info(f"Created story.md ({story_file.stat().st_size} bytes)")

    # User message
    user_message_file = iter_path / "user_message.txt"
    user_message = (user_message_file.read_text().strip()
                    if user_message_file.exists()
                    else "Story concept generated. Please review.")

    # Save metadata (V5 format)
    metadata = {
        "stage": "01-story",
        "iteration": iteration,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "based_on": inputs,
        "refined_from": refined_from,
        "rating": None,
        "result": f"01-story/{iteration}/story.md",
        "model": "claude-sonnet-4.5",
        "session_id": session_id,
        "source": "generated",
    }

    save_metadata(iter_path / "metadata.json", metadata)
    logger.info("Saved metadata")

    # Log event
    log_event(
        story_path, "stage_complete",
        {"action": "story_generated", "iteration": iteration,
         "result": f"01-story/{iteration}/story.md"},
        take=take_path.name, stage="01-story", iteration=iteration
    )

    print(f"\nGenerated: 01-story/{iteration}/story.md")

    return {
        "status": "success",
        "iteration": iteration,
        "result": f"01-story/{iteration}/story.md",
        "user_message": user_message,
    }


def _is_literary_adaptation(brief_text: str) -> bool:
    """Detect if an episode brief is a literary adaptation from story_pipeline.

    Literary adaptation briefs contain structured scene outlines and dialogue
    moments generated by stage S6 of the story pipeline.
    """
    if not brief_text:
        return False
    # Look for markers that S6 prepare-episode creates
    markers = [
        "Scene Outline",
        "Dialogue Moments",
        "Voice Direction",
        "Story Segment",
        "Character Guide",
        "Continuity Notes",
    ]
    matches = sum(1 for m in markers if m.lower() in brief_text.lower())
    return matches >= 2
