#!/usr/bin/python3
"""
Episode Generation Enforcement Hook (Global)

Ensures batch discipline during microdrama episode generation for ANY series:
1. Episodes must be written in batches of 5
2. Checkpoint must exist for previous batch before writing next
3. Works across all production projects in /recoil/

Usage: Configure in /recoil/.claude/settings.json as a PreToolUse hook
"""

import json
import sys
import re
from pathlib import Path

# Add engine tools to path for imports
_SCRIPT_DIR = Path(__file__).parent.resolve()
_ENGINE_TOOLS = _SCRIPT_DIR.parent.parent / 'tools'
if _ENGINE_TOOLS.exists():
    sys.path.insert(0, str(_ENGINE_TOOLS))
sys.path.insert(0, str(_SCRIPT_DIR.parent.parent.parent))  # CLAUDE_PROJECTS, for recoil.*
from recoil.core.paths import ProjectPaths

try:
    from engine_constants import GENERATION_BATCH_SIZE
except ImportError:
    GENERATION_BATCH_SIZE = 5

def get_project_root_from_path(filepath):
    """Find project root by walking up from the file path until we find state/ folder"""
    current = Path(filepath).parent
    while current != current.parent:
        if (current / "state").exists():
            return current
        current = current.parent
    return None

def get_episode_number(filepath):
    """Extract episode number from filepath like ep_042.md"""
    match = re.search(r'ep_(\d{3})\.md', filepath)
    if match:
        return int(match.group(1))
    return None

def get_batch_number(ep_num):
    """Episodes 1-5 = batch 1, 6-10 = batch 2, etc."""
    return ((ep_num - 1) // GENERATION_BATCH_SIZE) + 1

def check_checkpoint_exists(project_root, batch_num):
    """Check if checkpoint file exists for a given batch"""
    checkpoint_dir = ProjectPaths.from_root(project_root).checkpoints_dir
    if not checkpoint_dir.exists():
        return False
    # Look for batch_XX checkpoint (either batch_XX.json or batch_XX_checkpoint.json)
    patterns = [f"batch_{batch_num:02d}", f"batch_{batch_num:02d}_checkpoint"]
    for f in checkpoint_dir.iterdir():
        for pattern in patterns:
            if pattern in f.name:
                return True
    return False

def main():
    # Read hook input from stdin
    try:
        hook_input = json.load(sys.stdin)
    except (json.JSONDecodeError, ValueError, EOFError):
        # If no JSON input, allow (not a hook context)
        sys.exit(0)

    tool_name = hook_input.get("tool_name", "")
    tool_input = hook_input.get("tool_input", {})

    # Only check Write operations
    if tool_name != "Write":
        sys.exit(0)

    file_path = tool_input.get("file_path", "")

    # Only check episode files (pattern: /episodes/ep_XXX.md)
    if "/episodes/ep_" not in file_path or not file_path.endswith(".md"):
        sys.exit(0)

    ep_num = get_episode_number(file_path)
    if ep_num is None:
        sys.exit(0)

    batch_num = get_batch_number(ep_num)
    project_root = get_project_root_from_path(file_path)

    if project_root is None:
        # Can't find project (no state/ folder), allow but warn
        result = {
            "decision": "allow",
            "reason": f"No state/ folder found for {file_path} - proceeding without enforcement"
        }
        print(json.dumps(result))
        sys.exit(0)

    project_name = project_root.name

    # RULE 1: Overwrite guard — block writes to episodes that already exist
    # with a valid checkpoint for their batch (prevents duplicate generation
    # after context compaction). Checked FIRST, applies to ALL batches including batch 1.
    episode_file = Path(file_path)
    if episode_file.exists() and check_checkpoint_exists(project_root, batch_num):
        result = {
            "decision": "block",
            "reason": (
                f"BLOCKED: Episode {ep_num} already exists with valid checkpoint (batch {batch_num}). "
                f"To regenerate, delete the checkpoint first:\n"
                f"  rm {project_root}/state/checkpoints/batch_{batch_num:02d}_checkpoint.json\n"
                f"Or use --fresh flag to start clean."
            )
        }
        print(json.dumps(result))
        sys.exit(2)

    # RULE 2: First batch (eps 1-5) — no previous checkpoint needed
    if batch_num == 1:
        sys.exit(0)

    # RULE 3: Check that previous batch checkpoint exists
    prev_batch = batch_num - 1
    if not check_checkpoint_exists(project_root, prev_batch):
        result = {
            "decision": "block",
            "reason": f"ENFORCEMENT [{project_name}]: Cannot write episode {ep_num} (batch {batch_num}). Missing checkpoint for batch {prev_batch}. Run: python3 [project]/.claude/hooks/save_checkpoint.py {prev_batch}"
        }
        print(json.dumps(result))
        sys.exit(2)

    # All checks passed
    sys.exit(0)

if __name__ == "__main__":
    main()
