#!/usr/bin/env python3
"""
Project Promotion Tool
Promotes a development project to production status.

Usage:
    python promote_project.py <project_name>
    python promote_project.py leviathan

This will:
1. Create production folder structure
2. Generate series_bible.md from development docs
3. Generate episode_arc.md from structure_outline.md
4. Copy characters.md to bible/ (no transformation needed)
5. Create ORCHESTRATION.md
6. Initialize current_state.json
"""

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

# ANSI colors
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
CYAN = '\033[96m'
BOLD = '\033[1m'
RESET = '\033[0m'


def read_file(filepath):
    """Read file contents."""
    if not os.path.exists(filepath):
        return None
    with open(filepath, 'r', encoding='utf-8') as f:
        return f.read()


def write_file(filepath, content):
    """Write content to file, creating directories if needed."""
    os.makedirs(os.path.dirname(filepath), exist_ok=True)
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(content)
    print(f"  {GREEN}✓{RESET} Created: {filepath}")


def extract_section(content, header_pattern, end_pattern=None):
    """Extract a section from markdown content."""
    match = re.search(header_pattern, content, re.IGNORECASE)
    if not match:
        return ""

    start = match.end()
    if end_pattern:
        end_match = re.search(end_pattern, content[start:], re.IGNORECASE)
        if end_match:
            return content[start:start + end_match.start()].strip()

    # Find next same-level header
    next_header = re.search(r'\n##\s+', content[start:])
    if next_header:
        return content[start:start + next_header.start()].strip()

    return content[start:].strip()


def generate_series_bible(dev_path, project_name):
    """Generate series_bible.md from development documents."""
    thematic = read_file(dev_path / 'thematic_spine.md') or ""
    characters = read_file(dev_path / 'characters.md') or ""  # Updated: was character_arcs.md
    relationship = read_file(dev_path / 'relationship_map.md') or ""
    status = read_file(dev_path / 'STATUS.md') or ""

    # Extract key elements
    logline_match = re.search(r'Logline.*?>\s*"([^"]+)"', status, re.IGNORECASE | re.DOTALL)
    logline = logline_match.group(1) if logline_match else "[LOGLINE NOT FOUND]"

    conceit_match = re.search(r'Conceit.*?>\s*"([^"]+)"', status, re.IGNORECASE | re.DOTALL)
    conceit = conceit_match.group(1) if conceit_match else "[CONCEIT NOT FOUND]"

    bible = f"""# {project_name.upper()} — Series Bible

## THE HOOK

**Logline:**
> "{logline}"

**Surprising Conceit:**
> "{conceit}"

---

## THEMATIC CORE

"""
    # Add thematic content
    if thematic:
        # Extract key sections
        question_match = re.search(r'Thematic Question.*?>\s*"([^"]+)"', thematic, re.IGNORECASE | re.DOTALL)
        if question_match:
            bible += f'**The Question:** "{question_match.group(1)}"\n\n'

        really_about = re.search(r'Really About.*?>\s*"?([^"\n]+)"?', thematic, re.IGNORECASE | re.DOTALL)
        if really_about:
            bible += f'**This Is Really About:** {really_about.group(1).strip()}\n\n'

    bible += """---

## CHARACTERS

"""
    # Extract character info by role (generic — works for any project)
    if characters:
        # Find character sections by **Role:** field
        char_sections = re.split(r'\n(?=## [A-Z])', characters)
        for section in char_sections:
            role_match = re.search(r'\*\*Role:\*\*\s*(.*)', section)
            if not role_match:
                continue
            role_text = role_match.group(1).strip().lower()
            if 'protagonist' in role_text:
                bible += f"### PROTAGONIST\n\n{section[:1000]}...\n\n"
            elif 'anchor' in role_text or 'emotional anchor' in role_text:
                bible += f"### EMOTIONAL ANCHOR\n\n{section[:1000]}...\n\n"

    bible += """---

## THE RELATIONSHIP

"""
    if relationship:
        ache_match = re.search(r'Primary Ache.*?>\s*"([^"]+)"', relationship, re.IGNORECASE | re.DOTALL)
        if ache_match:
            bible += f'**The Primary Ache:** "{ache_match.group(1)}"\n\n'

    bible += f"""---

## WORLD

[See development documents for full world details]

---

*Generated: {datetime.now().strftime('%Y-%m-%d')}*
*Source: /projects/{project_name}/development/*
"""
    return bible


def generate_episode_arc(dev_path, project_name):
    """Generate episode_arc.md SKELETON from structure_outline.md.

    This creates a template with sequence headers and empty tables.
    The promote_agent fills in per-episode details (one-liners, cliffhangers).

    Structure:
    - 8 sequences with episode ranges
    - Per-sequence: Function, Thematic stance, Action beat count
    - Per-episode table: Ep | One-Line | Cliffhanger | Type | Intensity
    - Verification sections
    - Plant/payoff tracking
    """
    structure = read_file(dev_path / 'structure_outline.md') or ""
    plant_payoff = read_file(dev_path / 'plant_payoff_plan.md') or ""

    # Define 8-sequence structure
    sequences = [
        (1, "THE AWAKENING", 1, 8, "Establish world, protagonist, inciting incident"),
        (2, "FIRST COMPLICATIONS", 9, 15, "Question becomes personal, first test"),
        (3, "RISING ACTION", 16, 23, "Protagonist's stance tested, pressure builds"),
        (4, "MIDPOINT", 24, 30, "Question deepens, major turn"),
        (5, "COMPLICATIONS DEEPEN", 31, 38, "Pressure mounts, cracks appear"),
        (6, "ALL IS LOST", 39, 45, "Crisis point, question becomes urgent"),
        (7, "FINAL PUSH", 46, 53, "Resolution emerges through action"),
        (8, "RESOLUTION", 54, 60, "Answer is lived, not spoken"),
    ]

    arc = f"""# {project_name.upper()} — Episode Arc

## 60-Episode Roadmap

**Purpose:** This document tells the generation system what each episode must accomplish.

---

## HOW TO USE THIS DOCUMENT

1. Find the episode number you're about to write
2. Note the **MUST CONTAIN** beats — these are non-negotiable
3. Check **THREADS TO ADVANCE** — reference the state file for current status
4. Note **EMOTIONAL BEAT** requirements (💔 markers)

### Flexibility Markers
- 🔒 = Non-negotiable (plot-critical)
- 🎲 = Flexible (can be adjusted)
- ✨ = Opportunity for wildcard moment
- 💔 = Emotional beat required

### Cliffhanger Types
**Mid-Action:** M-PT (Physical Threat), M-CF (Confrontation), M-PU (Pursuit), M-CH (Choice), M-CT (Catch)
**Aftermath:** A-RE (Reveal), A-CO (Consequence), A-PS (Psychological), A-SI (Silent), A-CT (Catastrophe), A-DE (Decision)

---

"""
    # Generate sequence sections with empty tables
    for seq_num, seq_name, start_ep, end_ep, function in sequences:
        arc += f"""## SEQUENCE {seq_num}: {seq_name} (Ep {start_ep}-{end_ep})

**Function:** {function}
**Thematic:** [FILL: Thematic stance for this sequence]
**Action Beats:** [FILL: Count and episode numbers]

| Ep | One-Line | Cliffhanger | Type | Intensity |
|----|----------|-------------|------|-----------|
"""
        # Generate empty rows for each episode in sequence
        for ep in range(start_ep, end_ep + 1):
            arc += f"| {ep} | [FILL] | [FILL] | [FILL] | [FILL] |\n"

        arc += "\n---\n\n"

    # Verification sections
    arc += """## VERIFICATION

### Action Beat Distribution
[FILL: List episodes with action beats, verify 2-3 per sequence minimum]

### Emotional Beat Schedule
Required 💔 markers at: Ep 10, 15, 20, 26, 30, 32-33, 36, 42, 45, 50, 59-60
[FILL: Verify all emotional beats are assigned]

### Cliffhanger Distribution
Target: 70-85% Mid-Action, 15-30% Aftermath
[FILL: Calculate actual distribution]

### Intensity Curve
[FILL: Note intensity progression - should build with peaks at key episodes]

---

## PLANT/PAYOFF TRACKING

"""
    if plant_payoff:
        # Extract thread summary
        threads = re.findall(r'##\s*Thread\s*\d+[:\s]+([^\n]+)', plant_payoff)
        if threads:
            arc += "**Active Threads:**\n"
            for t in threads:
                arc += f"- {t.strip()}\n"
        arc += "\n[FILL: Map plant episodes and payoff episodes for each thread]\n"
    else:
        arc += "[FILL: Add plant/payoff tracking from development documents]\n"

    arc += f"""
---

**STATUS: SKELETON — Requires AI expansion during /promote**

*Generated: {datetime.now().strftime('%Y-%m-%d')}*
*Source: /projects/{project_name}/development/*
"""
    return arc


def copy_characters(dev_path):
    """Copy characters.md directly - no transformation needed.

    The characters.md file already contains everything needed for generation:
    - Behavioral DNA (on-screen behaviors, stress, signature lines)
    - Voice DNA (idiom, humor type, anti-patterns)
    - Character arcs (transformation, mask cracks)

    No transformation required - just copy to bible folder.
    """
    characters = read_file(dev_path / 'characters.md')
    if not characters:
        return None
    return characters


def generate_orchestration(project_name):
    """Generate ORCHESTRATION.md template."""
    return f"""# {project_name.upper()} — Orchestration

## Generation Protocol

### Before Each Batch
1. Read `current_state.json` for position
2. Read last 2 episodes for continuity
3. Check upcoming emotional beats
4. Check plant/payoff threads due

### During Generation
- Generate 5 episodes per batch
- Maintain cliffhanger variety (no 3+ same type)
- Track thread instances
- Mark emotional beats (💔)

### After Each Batch
1. Save episodes to `/episodes/`
2. Update `current_state.json`
3. Run `validate_batch.py`
4. Save checkpoint

---

## Batch Schedule

| Batch | Episodes | Key Events |
|-------|----------|------------|
| 1 | 1-5 | Setup, inciting incident |
| 2 | 6-10 | 💔 Ep 10: First crack |
| 3 | 11-15 | 💔 Ep 15: Point of no return |
| 4 | 16-20 | 💔 Ep 20: Turning point |
| 5 | 21-25 | Development |
| 6 | 26-30 | 💔 Ep 26, 💔 Ep 30: Midpoint |
| 7 | 31-35 | 💔 Ep 32-33: Identity crisis |
| 8 | 36-40 | 💔 Ep 36: Vulnerability |
| 9 | 41-45 | 💔 Ep 42, 💔 Ep 45: All is lost |
| 10 | 46-50 | 💔 Ep 50: Recovery |
| 11 | 51-55 | Final push |
| 12 | 56-60 | 💔 Ep 59-60: Catharsis |

---

*Generated: {datetime.now().strftime('%Y-%m-%d')}*
"""


def generate_initial_state(project_name):
    """Generate initial current_state.json."""
    return {
        "project": project_name,
        "last_episode": 0,
        "last_batch": 0,
        "generated": [],
        "cliffhanger_history": [],
        "emotional_beats_hit": [],
        "threads_planted": [],
        "threads_paid_off": [],
        "relationship_stage": "Transaction",
        "last_updated": datetime.now().isoformat()
    }


def promote_project(project_name, base_path='.', force=False):
    """Promote a project from development to production."""
    base_path = Path(base_path)
    dev_path = base_path / 'projects' / project_name / 'development'
    prod_path = base_path / 'projects' / project_name

    # Check development folder exists
    if not dev_path.exists():
        print(f"{RED}Error: Development folder not found: {dev_path}{RESET}")
        return False

    # Check if production folder already exists
    if prod_path.exists():
        print(f"{YELLOW}Warning: Production folder already exists: {prod_path}{RESET}")
        if force:
            pass  # Skip prompt, proceed with overwrite
        elif sys.stdin.isatty():
            response = input("Overwrite? (y/n): ")
            if response.lower() != 'y':
                print("Aborted.")
                return False
        else:
            print(f"{RED}Error: Production folder exists and running non-interactively. Use --force to overwrite.{RESET}")
            return False

    print(f"\n{BOLD}Promoting {project_name} to production...{RESET}\n")

    # Create folder structure
    folders = [
        prod_path / 'bible',
        prod_path / 'prompts',
        prod_path / 'state',
        prod_path / 'state' / 'checkpoints',
        prod_path / 'episodes'
    ]

    for folder in folders:
        os.makedirs(folder, exist_ok=True)
        print(f"  {GREEN}✓{RESET} Created: {folder}")

    # Generate files
    print(f"\n{BOLD}Generating production documents...{RESET}\n")

    # Series Bible
    bible = generate_series_bible(dev_path, project_name)
    write_file(prod_path / 'bible' / 'series_bible.md', bible)

    # Episode Arc
    arc = generate_episode_arc(dev_path, project_name)
    write_file(prod_path / 'bible' / 'episode_arc.md', arc)

    # Characters (direct copy - no transformation needed)
    characters = copy_characters(dev_path)
    if characters:
        write_file(prod_path / 'bible' / 'characters.md', characters)
    else:
        print(f"  {YELLOW}⚠{RESET} Warning: characters.md not found in development folder")

    # Orchestration
    orchestration = generate_orchestration(project_name)
    write_file(prod_path / 'ORCHESTRATION.md', orchestration)

    # Current State
    state = generate_initial_state(project_name)
    write_file(prod_path / 'state' / 'current_state.json', json.dumps(state, indent=2))

    # Copy development documents for reference
    print(f"\n{BOLD}Copying development documents...{RESET}\n")
    dev_docs = [
        'thematic_spine.md',
        'characters.md',  # Updated: was character_arcs.md
        'relationship_map.md',
        'structure_outline.md',
        'plant_payoff_plan.md'
    ]

    for doc in dev_docs:
        src = dev_path / doc
        if src.exists():
            content = read_file(src)
            write_file(prod_path / 'bible' / f'dev_{doc}', content)

    print(f"\n{'═' * 50}")
    print(f"{BOLD}{GREEN}✅ PROMOTION COMPLETE{RESET}")
    print(f"{'═' * 50}")
    print(f"\nProduction folder: {prod_path}")
    print(f"\nNext steps:")
    print(f"  1. Review generated bible documents")
    print(f"  2. Run: python validate_arc.py {prod_path}")
    print(f"  3. Begin episode generation")
    print()

    return True


def main():
    import argparse
    parser = argparse.ArgumentParser(description="Promote a development project to production")
    parser.add_argument("project_name", help="Name of the project to promote")
    parser.add_argument("--force", action="store_true", help="Overwrite existing production folder without prompting")
    args = parser.parse_args()

    # Determine base path
    script_dir = Path(__file__).parent
    base_path = script_dir.parent.parent  # Go up from tools to root

    success = promote_project(args.project_name, base_path, force=args.force)
    sys.exit(0 if success else 1)


if __name__ == '__main__':
    main()
