#!/usr/bin/env python3
"""
EVOLUTION CYCLE - Main Execution Engine
=======================================
Executes a single evolution cycle: generate PRD → execute → verify → learn.

Usage:
    python core/evolution/evolution_cycle.py --cycle-number=1 --max-prds=5
"""

import os
import sys
import time
import json
import argparse
import logging
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, Optional, List

# Add genesis root
GENESIS_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(GENESIS_ROOT))

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s | %(levelname)-8s | %(message)s',
    datefmt='%H:%M:%S'
)
logger = logging.getLogger(__name__)

# Suppress verbose warnings
import warnings
warnings.filterwarnings('ignore')


class EvolutionCycle:
    """
    Executes a single evolution cycle.

    Phases:
    1. Generate PRDs
    2. Execute PRDs (via Gemini swarm)
    3. Verify results
    4. Detect surprise and learn
    """

    def __init__(
        self,
        cycle_number: int,
        max_prds: int = 5,
        surprise_threshold: float = 0.5
    ):
        """Initialize evolution cycle."""
        self.cycle_number = cycle_number
        self.max_prds = max_prds
        self.surprise_threshold = surprise_threshold

        # Results tracking
        self.prds_generated = 0
        self.stories_completed = 0
        self.tests_created = 0
        self.surprise_detected = False

        # State file
        self.state_file = GENESIS_ROOT / "data" / "evolution_state.json"

    def run(self) -> Dict[str, Any]:
        """
        Run the complete evolution cycle.

        Returns:
            Cycle results dictionary
        """
        start_time = time.time()
        logger.info(f"========== EVOLUTION CYCLE {self.cycle_number} ==========")

        results = {
            'cycle_number': self.cycle_number,
            'start_time': datetime.now().isoformat(),
            'phases': {},
        }

        try:
            # Phase 1: Generate PRDs
            logger.info("[Phase 1/4] Generating PRDs...")
            gen_result = self._phase_generate()
            results['phases']['generate'] = gen_result
            self.prds_generated = gen_result.get('count', 0)

            # Phase 2: Execute PRDs
            logger.info("[Phase 2/4] Executing PRDs...")
            exec_result = self._phase_execute(gen_result.get('prds', []))
            results['phases']['execute'] = exec_result
            self.stories_completed = exec_result.get('stories', 0)

            # Phase 3: Verify results
            logger.info("[Phase 3/4] Verifying results...")
            verify_result = self._phase_verify(exec_result)
            results['phases']['verify'] = verify_result
            self.tests_created = verify_result.get('tests_created', 0)

            # Phase 4: Surprise detection and learning
            logger.info("[Phase 4/4] Analyzing for surprises...")
            learn_result = self._phase_learn(results)
            results['phases']['learn'] = learn_result
            self.surprise_detected = learn_result.get('surprise_detected', False)

        except Exception as e:
            logger.error(f"Cycle error: {e}")
            results['error'] = str(e)

        # Finalize
        elapsed = time.time() - start_time
        results['elapsed_seconds'] = elapsed
        results['end_time'] = datetime.now().isoformat()

        # Save state
        self._save_state(results)

        # Log summary
        self._log_summary()

        return results

    def _phase_generate(self) -> Dict[str, Any]:
        """Phase 1: Generate PRDs."""
        from core.evolution.prd_generator import PRDGenerator

        generator = PRDGenerator()
        prds = generator.generate(
            cycle_id=self.cycle_number,
            max_prds=self.max_prds
        )

        return {
            'count': len(prds),
            'prds': [str(p) for p in prds],
            'statistics': generator.get_statistics(),
        }

    def _phase_execute(self, prd_paths: List[str]) -> Dict[str, Any]:
        """Phase 2: Execute PRDs."""
        stories_completed = 0
        execution_results = []

        for prd_path in prd_paths:
            try:
                # Parse PRD for stories
                prd_content = Path(prd_path).read_text()
                story_count = prd_content.count('#### Story')

                # Simulate execution (in real implementation, this would dispatch to Gemini)
                logger.info(f"  Executing PRD with {story_count} stories...")
                time.sleep(1)  # Simulate work

                # Mark stories as completed (simplified)
                stories_completed += max(1, story_count)

                execution_results.append({
                    'prd': prd_path,
                    'stories': story_count,
                    'status': 'completed'
                })

            except Exception as e:
                logger.warning(f"  PRD execution error: {e}")
                execution_results.append({
                    'prd': prd_path,
                    'status': 'failed',
                    'error': str(e)
                })

        return {
            'stories': stories_completed,
            'results': execution_results,
        }

    def _phase_verify(self, exec_result: Dict) -> Dict[str, Any]:
        """Phase 3: Verify execution results."""
        tests_created = 0
        test_results = []

        # For each executed PRD, verify and create tests
        for result in exec_result.get('results', []):
            if result.get('status') == 'completed':
                # Simulate test creation
                tests_for_prd = result.get('stories', 1) * 2  # 2 tests per story
                tests_created += tests_for_prd

                test_results.append({
                    'prd': result.get('prd'),
                    'tests_created': tests_for_prd,
                    'pass_rate': 1.0,  # Assume passing
                })

        return {
            'tests_created': tests_created,
            'test_results': test_results,
            'overall_pass_rate': 1.0,
        }

    def _phase_learn(self, cycle_results: Dict) -> Dict[str, Any]:
        """Phase 4: Surprise detection and learning."""
        from core.evolution.surprise_detector import SurpriseDetector, Expectation, Outcome

        detector = SurpriseDetector()

        # Create expectation based on cycle settings
        expectation = Expectation(
            execution_time=30.0 * self.max_prds,  # 30s per PRD
            test_pass_rate=1.0,
            error_count=0,
            output_tokens=500 * self.max_prds,
            complexity_score=0.5,
        )

        # Create outcome from actual results
        elapsed = cycle_results.get('elapsed_seconds', 0)
        verify_phase = cycle_results.get('phases', {}).get('verify', {})

        outcome = Outcome(
            execution_time=elapsed,
            test_pass_rate=verify_phase.get('overall_pass_rate', 1.0),
            error_count=1 if 'error' in cycle_results else 0,
            output_tokens=len(json.dumps(cycle_results)),
            complexity_score=0.5,
        )

        # Detect surprise
        event = detector.detect_surprise(
            expectation,
            outcome,
            cycle_id=self.cycle_number,
            threshold=self.surprise_threshold
        )

        learn_result = {
            'surprise_detected': event is not None,
            'surprise_score': event.surprise_score if event else 0.0,
        }

        if event:
            learn_result['trigger_reason'] = event.trigger_reason
            learn_result['learning_action'] = event.learning_action
            logger.warning(f"  SURPRISE_EVENT: score={event.surprise_score:.3f}, reason={event.trigger_reason}")

        return learn_result

    def _save_state(self, results: Dict):
        """Save cycle state to file."""
        try:
            # Load existing state
            if self.state_file.exists():
                state = json.loads(self.state_file.read_text())
            else:
                state = {'cycles': []}

            # Append this cycle
            state['cycles'].append(results)
            state['last_cycle'] = self.cycle_number
            state['last_update'] = datetime.now().isoformat()

            # Keep only last 100 cycles
            state['cycles'] = state['cycles'][-100:]

            # Save
            self.state_file.parent.mkdir(parents=True, exist_ok=True)
            self.state_file.write_text(json.dumps(state, indent=2))

        except Exception as e:
            logger.warning(f"Failed to save state: {e}")

    def _log_summary(self):
        """Log cycle summary for bash script parsing."""
        print(f"PRDs generated: {self.prds_generated}")
        print(f"Stories completed: {self.stories_completed}")
        print(f"Tests created: {self.tests_created}")
        if self.surprise_detected:
            print("SURPRISE_EVENT")


def main():
    parser = argparse.ArgumentParser(description="Genesis Evolution Cycle")
    parser.add_argument('--cycle-number', type=int, default=1, help='Cycle number')
    parser.add_argument('--max-prds', type=int, default=5, help='Max PRDs to generate')
    parser.add_argument('--surprise-threshold', type=float, default=0.5, help='Surprise threshold')
    args = parser.parse_args()

    cycle = EvolutionCycle(
        cycle_number=args.cycle_number,
        max_prds=args.max_prds,
        surprise_threshold=args.surprise_threshold,
    )

    results = cycle.run()

    logger.info(f"Cycle {args.cycle_number} complete in {results.get('elapsed_seconds', 0):.1f}s")


if __name__ == '__main__':
    main()
