#!/usr/bin/env python3
"""
Orchestration Core - Game Assembly Project Management System

This module provides the core orchestration loop for managing game assembly projects,
including project creation, state management, result collection, evaluation, and serving.

Author: OrbisClean Orchestration System
Version: 1.0.0
"""

import json
import fcntl
import argparse
import sys
import uuid
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional, Any
from contextlib import contextmanager


# Constants
WORKSPACE_ROOT = Path(__file__).parent.parent
REGISTRY_PATH = WORKSPACE_ROOT / "registry.json"
PROJECTS_DIR = WORKSPACE_ROOT / "projects"
TEMPLATES_DIR = WORKSPACE_ROOT / "templates"
SCHEMAS_DIR = WORKSPACE_ROOT / "schemas"
SERVING_DIR = WORKSPACE_ROOT / "serving"

PROJECT_STATES = ["pending", "building", "evaluating", "passed", "failed"]


class OrchestratorError(Exception):
    """Base exception for orchestrator errors"""
    pass


class ProjectNotFoundError(OrchestratorError):
    """Raised when project is not found"""
    pass


class InvalidStateTransitionError(OrchestratorError):
    """Raised when invalid state transition is attempted"""
    pass


class ValidationError(OrchestratorError):
    """Raised when validation fails"""
    pass


@contextmanager
def locked_registry(mode='r'):
    """
    Context manager for thread-safe registry.json access with file locking.
    
    Args:
        mode: File open mode ('r' for read, 'r+' for write)
    
    Yields:
        dict: Registry data
    """
    REGISTRY_PATH.parent.mkdir(parents=True, exist_ok=True)
    
    # Ensure registry exists
    if not REGISTRY_PATH.exists():
        with open(REGISTRY_PATH, 'w') as f:
            json.dump({
                "$schema": "https://json-schema.org/draft-07/schema#",
                "version": "1.0.0",
                "description": "OrbisClean Orchestration Registry",
                "projects": [],
                "templates": {},
                "schemas": {},
                "metadata": {
                    "created": datetime.now().isoformat(),
                    "lastUpdated": datetime.now().isoformat(),
                    "schemaVersion": "1.0.0"
                }
            }, f, indent=2)
    
    with open(REGISTRY_PATH, mode) as f:
        # Acquire exclusive lock for writes, shared lock for reads
        lock_type = fcntl.LOCK_EX if '+' in mode or 'w' in mode else fcntl.LOCK_SH
        fcntl.flock(f.fileno(), lock_type)
        
        try:
            registry = json.load(f)
            yield registry
            
            # Write back if mode allows
            if '+' in mode or 'w' in mode:
                f.seek(0)
                f.truncate()
                registry['metadata']['lastUpdated'] = datetime.now().isoformat()
                json.dump(registry, f, indent=2)
        finally:
            fcntl.flock(f.fileno(), fcntl.LOCK_UN)


def validate_state_transition(current_state: str, new_state: str) -> bool:
    """
    Validate state transition according to project lifecycle.
    
    Valid transitions:
    - pending → building
    - building → evaluating
    - evaluating → passed/failed
    - failed → building (retry)
    - Any state → same state (idempotent)
    
    Args:
        current_state: Current project state
        new_state: Desired new state
    
    Returns:
        bool: True if transition is valid
    """
    # Allow idempotent transitions (same → same)
    if current_state == new_state:
        return True
    
    valid_transitions = {
        "pending": ["building"],
        "building": ["evaluating", "failed"],
        "evaluating": ["passed", "failed"],
        "failed": ["building"],  # Allow retry
        "passed": []  # Terminal state
    }
    
    return new_state in valid_transitions.get(current_state, [])


def generate_project_id(name: str) -> str:
    """Generate unique project ID from name"""
    base_id = name.lower().replace(' ', '-').replace('_', '-')
    # Add short UUID suffix for uniqueness
    suffix = str(uuid.uuid4())[:8]
    return f"{base_id}-{suffix}"


def render_template(template_path: Path, variables: Dict[str, Any]) -> str:
    """
    Simple template rendering (supports {{VAR}} placeholders).
    
    Args:
        template_path: Path to template file
        variables: Dictionary of variable substitutions
    
    Returns:
        str: Rendered template
    """
    with open(template_path, 'r') as f:
        content = f.read()
    
    # Simple variable substitution
    for key, value in variables.items():
        placeholder = f"{{{{{key}}}}}"
        content = content.replace(placeholder, str(value))
    
    return content


def create_project(
    name: str,
    battle_config: Optional[Path] = None,
    scenario_config: Optional[Path] = None,
    ui_config: Optional[Path] = None,
    assembly_config: Optional[Path] = None,
    template: str = "full-game",
    description: str = ""
) -> Dict[str, Any]:
    """
    Create a new game assembly project.
    
    Args:
        name: Project name
        battle_config: Path to battle engine configuration JSON
        scenario_config: Path to scenario data configuration JSON
        ui_config: Path to UI configuration JSON
        assembly_config: Path to game assembly configuration JSON
        template: Template type (battle-focused, story-driven, full-game)
        description: Project description
    
    Returns:
        dict: Created project metadata
    """
    project_id = generate_project_id(name)
    project_dir = PROJECTS_DIR / project_id
    
    # Create project directory structure
    project_dir.mkdir(parents=True, exist_ok=True)
    (project_dir / "output").mkdir(exist_ok=True)
    (project_dir / "iterations").mkdir(exist_ok=True)
    
    # Create project metadata
    project = {
        "id": project_id,
        "name": name,
        "description": description,
        "template": template,
        "status": "pending",
        "created": datetime.now().isoformat(),
        "lastUpdated": datetime.now().isoformat(),
        "iterations": 0,
        "maxIterations": 3,
        "modules": {},
        "evaluation": None,
        "outputPath": str(project_dir / "output"),
        "servingPath": None
    }
    
    # Load module configurations
    if battle_config and battle_config.exists():
        with open(battle_config) as f:
            project["modules"]["battleEngine"] = json.load(f)
    
    if scenario_config and scenario_config.exists():
        with open(scenario_config) as f:
            project["modules"]["scenarioData"] = json.load(f)
    
    if ui_config and ui_config.exists():
        with open(ui_config) as f:
            project["modules"]["uiConfig"] = json.load(f)
    
    if assembly_config and assembly_config.exists():
        with open(assembly_config) as f:
            project["modules"]["gameAssembly"] = json.load(f)
    
    # Create spec.json based on game-assembly.schema.json
    spec = {
        "assemblyName": project_id,
        "version": "1.0.0",
        "description": description,
        "modules": {
            "battleEngine": {
                "source": str(battle_config) if battle_config else "undefined",
                "version": "1.0.0",
                "enabled": battle_config is not None
            },
            "scenarioData": {
                "source": str(scenario_config) if scenario_config else "undefined",
                "version": "1.0.0",
                "enabled": scenario_config is not None
            },
            "uiConfig": {
                "source": str(ui_config) if ui_config else "undefined",
                "version": "1.0.0",
                "enabled": ui_config is not None
            }
        },
        "dependencies": {},
        "metadata": {
            "created": datetime.now().isoformat(),
            "author": "OrbisClean Orchestrator"
        }
    }
    
    with open(project_dir / "spec.json", 'w') as f:
        json.dump(spec, f, indent=2)
    
    # Generate PROJECT.md from template
    template_vars = {
        "PROJECT_NAME": name,
        "PROJECT_ID": project_id,
        "VERSION": "1.0.0",
        "CREATED_DATE": datetime.now().strftime("%Y-%m-%d"),
        "LAST_UPDATED": datetime.now().isoformat(),
        "TEMPLATE_TYPE": template,
        "PROJECT_DESCRIPTION": description or "No description provided",
        "PLATFORM": "web",
        "ORIENTATION": "adaptive",
        "TARGET_DEVICES": "modern browsers",
        "GENRE": "Tactical RPG",
        "ART_STYLE": "2D Pixel Art",
        "NARRATIVE_FOCUS": "balanced",
        "BATTLE_ENGINE_ENABLED": "Yes" if battle_config else "No",
        "SCENARIO_ENABLED": "Yes" if scenario_config else "No",
        "UI_ENABLED": "Yes" if ui_config else "No",
        "ASSEMBLY_ENABLED": "Yes" if assembly_config else "No",
        "EVALUATION_ENABLED": "No",
        "SCHEMA_VERSION": "1.0.0",
        "AUTHOR": "OrbisClean Orchestrator",
        "LICENSE": "MIT",
        "TAGS": "tactical-rpg, game-assembly"
    }
    
    template_path = TEMPLATES_DIR / "PROJECT.md.tmpl"
    if template_path.exists():
        project_md = render_template(template_path, template_vars)
        with open(project_dir / "PROJECT.md", 'w') as f:
            f.write(project_md)
    
    # Register project in registry
    with locked_registry('r+') as registry:
        registry["projects"].append(project)
    
    print(f"✅ Project created: {project_id}")
    print(f"   Name: {name}")
    print(f"   Template: {template}")
    print(f"   Directory: {project_dir}")
    
    return project


def get_project(project_name_or_id: str) -> Dict[str, Any]:
    """
    Retrieve project by name or ID.
    
    Args:
        project_name_or_id: Project name or ID
    
    Returns:
        dict: Project metadata
    
    Raises:
        ProjectNotFoundError: If project not found
    """
    with locked_registry('r') as registry:
        for project in registry["projects"]:
            if project["id"] == project_name_or_id or project["name"] == project_name_or_id:
                return project
    
    raise ProjectNotFoundError(f"Project not found: {project_name_or_id}")


def list_projects(status_filter: Optional[str] = None) -> List[Dict[str, Any]]:
    """
    List all projects, optionally filtered by status.
    
    Args:
        status_filter: Optional status filter (pending, building, evaluating, passed, failed)
    
    Returns:
        list: List of project metadata dictionaries
    """
    with locked_registry('r') as registry:
        projects = registry["projects"]
        
        if status_filter:
            projects = [p for p in projects if p["status"] == status_filter]
        
        return projects


def update_status(project_name_or_id: str, new_status: str, rollback_on_error: bool = True) -> Dict[str, Any]:
    """
    Update project status with validation and optional rollback.
    
    Args:
        project_name_or_id: Project name or ID
        new_status: New status (pending, building, evaluating, passed, failed)
        rollback_on_error: Rollback state on error
    
    Returns:
        dict: Updated project metadata
    
    Raises:
        InvalidStateTransitionError: If transition is invalid
        ProjectNotFoundError: If project not found
    """
    if new_status not in PROJECT_STATES:
        raise ValueError(f"Invalid status: {new_status}. Must be one of {PROJECT_STATES}")
    
    with locked_registry('r+') as registry:
        project = None
        for p in registry["projects"]:
            if p["id"] == project_name_or_id or p["name"] == project_name_or_id:
                project = p
                break
        
        if not project:
            raise ProjectNotFoundError(f"Project not found: {project_name_or_id}")
        
        old_status = project["status"]
        
        # Validate transition
        if not validate_state_transition(old_status, new_status):
            if rollback_on_error:
                raise InvalidStateTransitionError(
                    f"Invalid transition: {old_status} → {new_status}"
                )
            else:
                print(f"⚠️  Warning: Forcing invalid transition {old_status} → {new_status}")
        
        # Update status
        project["status"] = new_status
        project["lastUpdated"] = datetime.now().isoformat()
        
        print(f"✅ Status updated: {project['name']} ({old_status} → {new_status})")
        
        return project


def collect_result(
    project_name_or_id: str,
    output_dir: Path,
    validate_output: bool = True
) -> Dict[str, Any]:
    """
    Collect subagent results and store in project output directory.
    
    Args:
        project_name_or_id: Project name or ID
        output_dir: Directory containing subagent output
        validate_output: Validate OUTPUT.json against schema
    
    Returns:
        dict: Collection summary
    
    Raises:
        ProjectNotFoundError: If project not found
        ValidationError: If output validation fails
    """
    project = get_project(project_name_or_id)
    project_output_dir = Path(project["outputPath"])
    
    # Ensure output directory exists
    project_output_dir.mkdir(parents=True, exist_ok=True)
    
    collected_files = []
    output_json_path = output_dir / "OUTPUT.json"
    
    # Parse and validate OUTPUT.json
    if output_json_path.exists():
        with open(output_json_path) as f:
            output_data = json.load(f)
        
        if validate_output:
            # Basic validation
            required_fields = ["status", "timestamp"]
            missing = [f for f in required_fields if f not in output_data]
            if missing:
                raise ValidationError(f"OUTPUT.json missing required fields: {missing}")
        
        # Copy OUTPUT.json
        dest_path = project_output_dir / f"OUTPUT_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        with open(dest_path, 'w') as f:
            json.dump(output_data, f, indent=2)
        collected_files.append(str(dest_path))
    
    # Copy all files from output directory
    import shutil
    for item in output_dir.iterdir():
        if item.is_file() and item.name != "OUTPUT.json":
            dest = project_output_dir / item.name
            shutil.copy2(item, dest)
            collected_files.append(str(dest))
    
    summary = {
        "project": project["id"],
        "collected": len(collected_files),
        "files": collected_files,
        "timestamp": datetime.now().isoformat()
    }
    
    print(f"✅ Collected {len(collected_files)} files to {project_output_dir}")
    
    return summary


def evaluate_project(project_name_or_id: str, evaluation_config: Optional[Path] = None) -> Dict[str, Any]:
    """
    Run project evaluation and update status based on results.
    
    Args:
        project_name_or_id: Project name or ID
        evaluation_config: Optional path to evaluation configuration JSON
    
    Returns:
        dict: Evaluation results
    """
    project = get_project(project_name_or_id)
    
    # Ensure project is in evaluating state (transition from building if needed)
    if project["status"] == "pending":
        update_status(project_name_or_id, "building")
    if project["status"] == "building":
        update_status(project_name_or_id, "evaluating")
    elif project["status"] != "evaluating":
        update_status(project_name_or_id, "evaluating")
    
    # Load evaluation configuration
    eval_config = {}
    if evaluation_config and evaluation_config.exists():
        with open(evaluation_config) as f:
            eval_config = json.load(f)
    
    # Perform evaluation (simplified - real implementation would run tests)
    evaluation_result = {
        "project": project["id"],
        "timestamp": datetime.now().isoformat(),
        "evaluationType": eval_config.get("evaluationType", "automated"),
        "criteria": [],
        "overallScore": 0.0,
        "passed": False,
        "issues": [],
        "recommendations": []
    }
    
    # Check if output exists
    project_output_dir = Path(project["outputPath"])
    output_files = list(project_output_dir.glob("*.json"))
    
    if not output_files:
        evaluation_result["passed"] = False
        evaluation_result["issues"].append("No output files found")
        new_status = "failed"
    else:
        # Simple pass/fail based on presence of output
        evaluation_result["passed"] = True
        evaluation_result["overallScore"] = 100.0
        new_status = "passed"
    
    # Save evaluation result
    eval_path = project_output_dir / "EVALUATION.json"
    with open(eval_path, 'w') as f:
        json.dump(evaluation_result, f, indent=2)
    
    # Update project with evaluation results
    with locked_registry('r+') as registry:
        for p in registry["projects"]:
            if p["id"] == project["id"]:
                p["evaluation"] = evaluation_result
                break
    
    # Update final status
    update_status(project_name_or_id, new_status)
    
    status_icon = "✅" if evaluation_result["passed"] else "❌"
    print(f"{status_icon} Evaluation complete: {project['name']}")
    print(f"   Status: {new_status.upper()}")
    print(f"   Score: {evaluation_result['overallScore']}")
    
    return evaluation_result


def create_iteration(project_name_or_id: str, feedback: str) -> Dict[str, Any]:
    """
    Create iteration feedback for failed project.
    
    Args:
        project_name_or_id: Project name or ID
        feedback: Feedback text for next iteration
    
    Returns:
        dict: Iteration metadata
    
    Raises:
        OrchestratorError: If max iterations exceeded
    """
    project = get_project(project_name_or_id)
    
    # Increment iteration counter
    with locked_registry('r+') as registry:
        for p in registry["projects"]:
            if p["id"] == project["id"]:
                p["iterations"] += 1
                project = p
                break
    
    iteration_num = project["iterations"]
    max_iterations = project["maxIterations"]
    
    # Check if max iterations exceeded
    escalation = iteration_num >= max_iterations
    
    # Create ITERATION.md
    project_dir = PROJECTS_DIR / project["id"]
    iterations_dir = project_dir / "iterations"
    iteration_path = iterations_dir / f"ITERATION_{iteration_num}.md"
    
    iteration_content = f"""# Iteration {iteration_num}/{max_iterations}

**Project:** {project['name']}  
**Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}  
**Status:** {'⚠️ ESCALATION REQUIRED' if escalation else '🔄 Retry'}

## Feedback

{feedback}

## Next Steps

{'This project has exceeded the maximum iteration limit. Manual intervention required.' if escalation else 'Address the feedback above and rebuild.'}

---
_Generated by Orchestrator Core_
"""
    
    with open(iteration_path, 'w') as f:
        f.write(iteration_content)
    
    # Update status back to building for retry
    if not escalation:
        update_status(project_name_or_id, "building")
    
    iteration_data = {
        "iteration": iteration_num,
        "maxIterations": max_iterations,
        "escalation": escalation,
        "feedback": feedback,
        "timestamp": datetime.now().isoformat(),
        "iterationFile": str(iteration_path)
    }
    
    status_icon = "⚠️" if escalation else "🔄"
    print(f"{status_icon} Iteration {iteration_num}/{max_iterations} created for {project['name']}")
    if escalation:
        print("   ⚠️  Max iterations exceeded - escalation required")
    
    return iteration_data


def resume_project(project_name_or_id: str) -> Dict[str, Any]:
    """
    Check current phase of a project based on artifact files and suggest next step.
    
    Args:
        project_name_or_id: Project name or ID
    
    Returns:
        dict: Resume information with currentPhase, artifacts, and nextStep
    """
    project = get_project(project_name_or_id)
    project_dir = PROJECTS_DIR / project["id"]
    
    # Phase artifact detection
    phase_artifacts = {
        0: ["GDD.md", "battle-slots.json", "character-registry.json"],
        1: ["scenario.json"],
        2: [],  # design-spec-*.md checked separately
        3: ["battle-adapter.js"],
        4: ["index.html"],
        5: ["e2e-result.json"],
    }
    
    phase_names = {
        0: "Director",
        1: "Scenario",
        2: "Game Design Review",
        3: "Battle Adapter",
        4: "UI Integration",
        5: "E2E Validation",
    }
    
    # Check which phases are complete
    completed_phases = []
    existing_artifacts = []
    missing_artifacts = []
    
    for phase_num, artifacts in phase_artifacts.items():
        if phase_num == 2:
            # Special: check for design-spec-*.md
            design_specs = list(project_dir.glob("design-spec-*.md"))
            if design_specs:
                completed_phases.append(phase_num)
                existing_artifacts.extend([str(f.name) for f in design_specs])
            else:
                missing_artifacts.append("design-spec-{adapter}.md")
        else:
            all_present = True
            for artifact in artifacts:
                if (project_dir / artifact).exists():
                    existing_artifacts.append(artifact)
                else:
                    all_present = False
                    missing_artifacts.append(artifact)
            if all_present and artifacts:
                completed_phases.append(phase_num)
    
    # Check E2E pass status
    e2e_passed = False
    e2e_result_path = project_dir / "e2e-result.json"
    if e2e_result_path.exists():
        try:
            with open(e2e_result_path) as f:
                e2e_data = json.load(f)
            e2e_passed = e2e_data.get("status") == "PASS"
        except (json.JSONDecodeError, KeyError):
            pass
    
    # Determine current phase
    if e2e_passed:
        current_phase = 5
        next_step = "serve"
        next_desc = "E2E PASS — `orchestrator.py serve --project {id}` 실행"
    elif 4 in completed_phases:
        current_phase = 4
        next_step = "phase5"
        next_desc = "Phase 5: E2E Validation 발주"
    elif 3 in completed_phases and 1 in completed_phases:
        current_phase = 3
        next_step = "phase4"
        next_desc = "Phase 4: UI Integration 발주"
    elif 2 in completed_phases:
        current_phase = 2
        next_step = "phase3"
        next_desc = "Phase 3: Battle Adapter 발주"
    elif 0 in completed_phases:
        current_phase = 0
        # Phase 1 and 2 can run in parallel
        pending = []
        if 1 not in completed_phases:
            pending.append("Phase 1: Scenario")
        if 2 not in completed_phases:
            pending.append("Phase 2: Game Design Review")
        next_step = "phase1+2"
        next_desc = f"발주 필요: {', '.join(pending)}"
    else:
        current_phase = -1
        next_step = "phase0"
        next_desc = "Phase 0: Director 발주"
    
    # Update currentPhase in registry
    with locked_registry('r+') as registry:
        for p in registry["projects"]:
            if p["id"] == project["id"]:
                p["currentPhase"] = current_phase
                break
    
    result = {
        "project": project["id"],
        "name": project["name"],
        "status": project["status"],
        "currentPhase": current_phase,
        "currentPhaseName": phase_names.get(current_phase, "Not Started"),
        "completedPhases": sorted(completed_phases),
        "existingArtifacts": existing_artifacts,
        "missingArtifacts": missing_artifacts,
        "nextStep": next_step,
        "nextDescription": next_desc,
        "e2ePassed": e2e_passed,
    }
    
    print(f"\n📊 Resume Info: {project['name']}")
    print(f"   Current Phase: {current_phase} ({phase_names.get(current_phase, 'Not Started')})")
    print(f"   Completed: {[f'{n}({phase_names[n]})' for n in sorted(completed_phases)]}")
    print(f"   Existing: {existing_artifacts}")
    print(f"   Missing: {missing_artifacts}")
    print(f"   ➡️  Next: {next_desc}")
    
    return result


def register_serving(project_name_or_id: str) -> Dict[str, Any]:
    """
    Register passed project for serving via symbolic link.
    
    Args:
        project_name_or_id: Project name or ID
    
    Returns:
        dict: Serving registration metadata
    
    Raises:
        OrchestratorError: If project has not passed evaluation
    """
    project = get_project(project_name_or_id)
    
    if project["status"] != "passed":
        raise OrchestratorError(
            f"Cannot serve project '{project['name']}' - status is '{project['status']}' (must be 'passed')"
        )
    
    # Create serving directory
    SERVING_DIR.mkdir(parents=True, exist_ok=True)
    
    # Create symbolic link
    project_output_dir = Path(project["outputPath"])
    serving_link = SERVING_DIR / project["id"]
    
    # Remove existing link if present
    if serving_link.exists() or serving_link.is_symlink():
        serving_link.unlink()
    
    serving_link.symlink_to(project_output_dir, target_is_directory=True)
    
    # Update project with serving path
    with locked_registry('r+') as registry:
        for p in registry["projects"]:
            if p["id"] == project["id"]:
                p["servingPath"] = str(serving_link)
                break
    
    serving_data = {
        "project": project["id"],
        "servingPath": str(serving_link),
        "outputPath": str(project_output_dir),
        "timestamp": datetime.now().isoformat()
    }
    
    print(f"✅ Project '{project['name']}' registered for serving")
    print(f"   Serving path: {serving_link}")
    
    return serving_data


def main():
    """CLI entry point"""
    parser = argparse.ArgumentParser(
        description="OrbisClean Orchestration Core - Game Assembly Project Manager",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Create new project
  %(prog)s create --name "dark-tactics" --battle battle.json --scenario scenario.json
  
  # Check project status
  %(prog)s status --project dark-tactics
  
  # List all projects
  %(prog)s list
  
  # Collect results
  %(prog)s collect --project dark-tactics --output-dir ./results/
  
  # Evaluate project
  %(prog)s evaluate --project dark-tactics
  
  # Create iteration with feedback
  %(prog)s iterate --project dark-tactics --feedback "Balance adjustments needed"
  
  # Register for serving
  %(prog)s serve --project dark-tactics
        """
    )
    
    subparsers = parser.add_subparsers(dest='command', help='Command to execute')
    
    # Create command
    create_parser = subparsers.add_parser('create', help='Create new project')
    create_parser.add_argument('--name', required=True, help='Project name')
    create_parser.add_argument('--battle', type=Path, help='Battle engine config JSON')
    create_parser.add_argument('--scenario', type=Path, help='Scenario data config JSON')
    create_parser.add_argument('--ui', type=Path, help='UI config JSON')
    create_parser.add_argument('--assembly', type=Path, help='Game assembly config JSON')
    create_parser.add_argument('--template', default='full-game', 
                               choices=['battle-focused', 'story-driven', 'full-game'],
                               help='Project template')
    create_parser.add_argument('--description', default='', help='Project description')
    
    # Status command
    status_parser = subparsers.add_parser('status', help='Get project status')
    status_parser.add_argument('--project', required=True, help='Project name or ID')
    
    # List command
    list_parser = subparsers.add_parser('list', help='List all projects')
    list_parser.add_argument('--status', choices=PROJECT_STATES, help='Filter by status')
    list_parser.add_argument('--json', action='store_true', help='Output as JSON')
    
    # Collect command
    collect_parser = subparsers.add_parser('collect', help='Collect subagent results')
    collect_parser.add_argument('--project', required=True, help='Project name or ID')
    collect_parser.add_argument('--output-dir', type=Path, required=True, 
                                help='Directory containing subagent output')
    collect_parser.add_argument('--no-validate', action='store_true', 
                                help='Skip OUTPUT.json validation')
    
    # Evaluate command
    eval_parser = subparsers.add_parser('evaluate', help='Evaluate project')
    eval_parser.add_argument('--project', required=True, help='Project name or ID')
    eval_parser.add_argument('--config', type=Path, help='Evaluation config JSON')
    
    # Iterate command
    iterate_parser = subparsers.add_parser('iterate', help='Create iteration with feedback')
    iterate_parser.add_argument('--project', required=True, help='Project name or ID')
    iterate_parser.add_argument('--feedback', required=True, help='Feedback text')
    
    # Resume command
    resume_parser = subparsers.add_parser('resume', help='Check project phase and suggest next step')
    resume_parser.add_argument('--project', required=True, help='Project name or ID')
    
    # Update-status command
    update_status_parser = subparsers.add_parser('update-status', help='Update project status')
    update_status_parser.add_argument('--project', required=True, help='Project name or ID')
    update_status_parser.add_argument('--status', required=True, choices=PROJECT_STATES, help='New status')
    
    # Serve command
    serve_parser = subparsers.add_parser('serve', help='Register project for serving')
    serve_parser.add_argument('--project', required=True, help='Project name or ID')
    
    args = parser.parse_args()
    
    if not args.command:
        parser.print_help()
        return 1
    
    try:
        if args.command == 'create':
            create_project(
                name=args.name,
                battle_config=args.battle,
                scenario_config=args.scenario,
                ui_config=args.ui,
                assembly_config=args.assembly,
                template=args.template,
                description=args.description
            )
        
        elif args.command == 'status':
            project = get_project(args.project)
            print(f"\n📊 Project Status: {project['name']}")
            print(f"   ID: {project['id']}")
            print(f"   Status: {project['status'].upper()}")
            print(f"   Created: {project['created']}")
            print(f"   Last Updated: {project['lastUpdated']}")
            print(f"   Iterations: {project['iterations']}/{project['maxIterations']}")
            if project.get('evaluation'):
                eval_data = project['evaluation']
                print(f"   Evaluation: {'✅ PASSED' if eval_data.get('passed') else '❌ FAILED'}")
                print(f"   Score: {eval_data.get('overallScore', 'N/A')}")
        
        elif args.command == 'list':
            projects = list_projects(status_filter=args.status)
            
            if args.json:
                print(json.dumps(projects, indent=2))
            else:
                print(f"\n📋 Projects ({len(projects)} total):\n")
                for p in projects:
                    status_icons = {
                        "pending": "⏳",
                        "building": "🔨",
                        "evaluating": "🔍",
                        "passed": "✅",
                        "failed": "❌"
                    }
                    icon = status_icons.get(p['status'], '❓')
                    print(f"  {icon} {p['name']} ({p['id']})")
                    print(f"     Status: {p['status']} | Iterations: {p['iterations']}/{p['maxIterations']}")
        
        elif args.command == 'collect':
            collect_result(
                project_name_or_id=args.project,
                output_dir=args.output_dir,
                validate_output=not args.no_validate
            )
        
        elif args.command == 'evaluate':
            evaluate_project(
                project_name_or_id=args.project,
                evaluation_config=args.config
            )
        
        elif args.command == 'iterate':
            create_iteration(
                project_name_or_id=args.project,
                feedback=args.feedback
            )
        
        elif args.command == 'resume':
            resume_project(project_name_or_id=args.project)
        
        elif args.command == 'update-status':
            update_status(
                project_name_or_id=args.project,
                new_status=args.status
            )
        
        elif args.command == 'serve':
            register_serving(project_name_or_id=args.project)
        
        return 0
    
    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        return 1


if __name__ == '__main__':
    sys.exit(main())
