#!/usr/bin/env python3
"""
Safe Executor for Strategy Code
Provides a sandboxed environment for executing user-provided betting strategy code.
"""

import ast
import sys
from typing import Dict, List, Any
from contextlib import contextmanager
import signal

class TimeoutError(Exception):
    pass

class SafeExecutionError(Exception):
    pass

class SafeExecutor:
    """
    Safely execute user-provided strategy code in a restricted environment.
    """
    
    ALLOWED_BUILTINS = {
        'abs', 'all', 'any', 'bool', 'dict', 'enumerate', 'filter', 
        'float', 'int', 'len', 'list', 'map', 'max', 'min', 'range',
        'round', 'set', 'sorted', 'str', 'sum', 'tuple', 'zip',
        'True', 'False', 'None'
    }
    
    FORBIDDEN_NODES = {
        ast.Import, ast.ImportFrom,
        ast.Global, ast.Nonlocal,
    }
    
    def __init__(self, timeout=30, max_iterations=100000):
        self.timeout = timeout
        self.max_iterations = max_iterations
    
    def _validate_code(self, code):
        try:
            tree = ast.parse(code)
        except SyntaxError as e:
            raise SafeExecutionError(f'Syntax error in strategy code: {e}')
        
        for node in ast.walk(tree):
            if type(node) in self.FORBIDDEN_NODES:
                raise SafeExecutionError(
                    f'Forbidden operation in strategy code: {type(node).__name__}'
                )
            
            if isinstance(node, ast.Attribute):
                if node.attr.startswith('_'):
                    raise SafeExecutionError(
                        f'Access to private attributes is forbidden: {node.attr}'
                    )
            
            if isinstance(node, ast.Call):
                if isinstance(node.func, ast.Name):
                    if node.func.id in ('eval', 'exec', 'compile', 'open', 
                                        '__import__', 'input', 'getattr',
                                        'setattr', 'delattr', 'globals', 'locals'):
                        raise SafeExecutionError(
                            f'Forbidden function call: {node.func.id}'
                        )
    
    def _create_safe_globals(self):
        builtins_dict = __builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__
        safe_builtins = {}
        for name in self.ALLOWED_BUILTINS:
            if name in builtins_dict:
                safe_builtins[name] = builtins_dict[name]
        
        safe_builtins['True'] = True
        safe_builtins['False'] = False
        safe_builtins['None'] = None
        
        return {
            '__builtins__': safe_builtins,
            '__name__': '__strategy__',
            '__doc__': None,
        }
    
    @contextmanager
    def _timeout_context(self, seconds):
        def timeout_handler(signum, frame):
            raise TimeoutError(f'Strategy execution timed out after {seconds} seconds')
        
        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        signal.alarm(seconds)
        
        try:
            yield
        finally:
            signal.alarm(0)
            signal.signal(signal.SIGALRM, old_handler)
    
    def execute_strategy(self, code, games, players, teams, params):
        self._validate_code(code)
        
        safe_globals = self._create_safe_globals()
        safe_locals = {}
        
        try:
            with self._timeout_context(self.timeout):
                exec(code, safe_globals, safe_locals)
            
            execute_fn = safe_locals.get('execute')
            if not callable(execute_fn):
                raise SafeExecutionError(
                    "Strategy code must define an 'execute(game)' function"
                )
            
            bets = []
            for i, game in enumerate(games):
                if i >= self.max_iterations:
                    break
                
                try:
                    result = execute_fn(game)
                    if result and result.get('bet'):
                        bets.append({
                            'game_id': game.get('id', i),
                            'game': game,
                            'prediction': result.get('pick', 'home'),
                            'bet_type': result.get('bet_type', 'spread'),
                            'confidence': result.get('confidence', 0.5),
                        })
                except Exception as e:
                    continue
            
            return {'bets': bets, 'games': games}
            
        except TimeoutError:
            raise SafeExecutionError(
                f'Strategy execution timed out after {self.timeout} seconds'
            )
        except Exception as e:
            raise SafeExecutionError(f'Strategy execution failed: {str(e)}')
