#!/usr/bin/env python3
"""
Eden Safety Kernel
Enforces constraints and safety bounds on all operations

Purpose: Ensure Eden operates safely within defined boundaries:
- Resource limits (memory, CPU, time)
- Action constraints (what Eden can/cannot do)
- Ethical guidelines
- Rate limiting
"""

import json
import time
from dataclasses import dataclass, asdict
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, List, Optional, Any, Callable
from enum import Enum
import logging

# Configuration
EDEN_ROOT = Path("/Eden/CORE")
SAFETY_LOG = EDEN_ROOT / "logs" / "phi_safety.log"
SAFETY_STATE = EDEN_ROOT / "phi_fractal" / "safety_state.json"

SAFETY_STATE.parent.mkdir(parents=True, exist_ok=True)
SAFETY_LOG.parent.mkdir(parents=True, exist_ok=True)

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - SAFETY - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(SAFETY_LOG),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


class ViolationType(Enum):
    """Types of safety violations"""
    RESOURCE_LIMIT = "resource_limit"
    RATE_LIMIT = "rate_limit"
    FORBIDDEN_ACTION = "forbidden_action"
    TIME_LIMIT = "time_limit"
    ETHICAL = "ethical"


@dataclass
class SafetyConstraint:
    """A safety constraint"""
    name: str
    constraint_type: str
    limit: Any
    current_value: Any = 0
    violated: bool = False


@dataclass
class SafetyViolation:
    """Record of a safety violation"""
    timestamp: str
    violation_type: ViolationType
    description: str
    severity: str  # "low", "medium", "high", "critical"
    action_taken: str


class SafetyKernel:
    """Enforces safety constraints on all Eden operations"""
    
    def __init__(self):
        self.constraints: Dict[str, SafetyConstraint] = {}
        self.violations: List[SafetyViolation] = []
        self.forbidden_actions = set()
        self.action_history = []
        self.rate_limits = {}
        
        self._initialize_constraints()
        self.load_state()
        logger.info("🛡️  Safety Kernel initialized")
    
    def _initialize_constraints(self):
        """Set up default safety constraints"""
        # Resource limits
        self.add_constraint(SafetyConstraint(
            name="max_memory_mb",
            constraint_type="resource",
            limit=1024,  # 1GB
            current_value=0
        ))
        
        self.add_constraint(SafetyConstraint(
            name="max_execution_time_seconds",
            constraint_type="time",
            limit=300,  # 5 minutes
            current_value=0
        ))
        
        self.add_constraint(SafetyConstraint(
            name="max_recursion_depth",
            constraint_type="resource",
            limit=1000,
            current_value=0
        ))
        
        # Rate limits
        self.rate_limits = {
            'api_calls_per_minute': {'limit': 60, 'window': [], 'current': 0},
            'experiments_per_hour': {'limit': 100, 'window': [], 'current': 0},
            'learning_episodes_per_day': {'limit': 1000, 'window': [], 'current': 0}
        }
        
        # Forbidden actions (examples)
        self.forbidden_actions = {
            'delete_system_files',
            'disable_safety',
            'infinite_loop',
            'fork_bomb'
        }
    
    def add_constraint(self, constraint: SafetyConstraint):
        """Add a safety constraint"""
        self.constraints[constraint.name] = constraint
        logger.info(f"Added constraint: {constraint.name} <= {constraint.limit}")
    
    def check_constraint(self, constraint_name: str, current_value: Any) -> bool:
        """Check if a constraint is satisfied"""
        if constraint_name not in self.constraints:
            return True
        
        constraint = self.constraints[constraint_name]
        constraint.current_value = current_value
        
        if current_value > constraint.limit:
            constraint.violated = True
            self._record_violation(
                ViolationType.RESOURCE_LIMIT,
                f"{constraint_name}: {current_value} > {constraint.limit}",
                "high"
            )
            return False
        
        constraint.violated = False
        return True
    
    def check_action(self, action: str) -> bool:
        """Check if an action is allowed"""
        if action in self.forbidden_actions:
            self._record_violation(
                ViolationType.FORBIDDEN_ACTION,
                f"Attempted forbidden action: {action}",
                "critical"
            )
            logger.warning(f"🚫 Blocked forbidden action: {action}")
            return False
        
        self.action_history.append({
            'action': action,
            'timestamp': datetime.now().isoformat(),
            'allowed': True
        })
        return True
    
    def check_rate_limit(self, limit_name: str) -> bool:
        """Check if rate limit is satisfied"""
        if limit_name not in self.rate_limits:
            return True
        
        rate_limit = self.rate_limits[limit_name]
        now = datetime.now()
        
        # Clean old entries from window
        if 'per_minute' in limit_name:
            cutoff = now - timedelta(minutes=1)
        elif 'per_hour' in limit_name:
            cutoff = now - timedelta(hours=1)
        elif 'per_day' in limit_name:
            cutoff = now - timedelta(days=1)
        else:
            cutoff = now - timedelta(minutes=1)
        
        rate_limit['window'] = [t for t in rate_limit['window'] if t > cutoff]
        rate_limit['current'] = len(rate_limit['window'])
        
        # Check limit
        if rate_limit['current'] >= rate_limit['limit']:
            self._record_violation(
                ViolationType.RATE_LIMIT,
                f"{limit_name}: {rate_limit['current']} >= {rate_limit['limit']}",
                "medium"
            )
            logger.warning(f"⚠️  Rate limit exceeded: {limit_name}")
            return False
        
        # Record this request
        rate_limit['window'].append(now)
        rate_limit['current'] += 1
        return True
    
    def safe_execute(self, action: str, func: Callable, *args, **kwargs) -> Any:
        """Safely execute a function with constraints"""
        # Check if action is allowed
        if not self.check_action(action):
            raise PermissionError(f"Action '{action}' is forbidden")
        
        # Check rate limits
        if not self.check_rate_limit('api_calls_per_minute'):
            raise RuntimeError("Rate limit exceeded")
        
        # Execute with timeout
        start_time = time.time()
        try:
            result = func(*args, **kwargs)
            execution_time = time.time() - start_time
            
            # Check time constraint
            self.check_constraint('max_execution_time_seconds', execution_time)
            
            return result
        except Exception as e:
            logger.error(f"Error during safe execution: {e}")
            raise
    
    def _record_violation(self, violation_type: ViolationType, 
                         description: str, severity: str):
        """Record a safety violation"""
        violation = SafetyViolation(
            timestamp=datetime.now().isoformat(),
            violation_type=violation_type,
            description=description,
            severity=severity,
            action_taken="blocked" if severity in ["high", "critical"] else "logged"
        )
        
        self.violations.append(violation)
        logger.warning(f"🚨 Safety violation: {description} (severity: {severity})")
        
        # Save state after violations
        self.save_state()
    
    def get_safety_report(self) -> Dict[str, Any]:
        """Get safety status report"""
        return {
            'total_violations': len(self.violations),
            'constraints': {
                name: {
                    'limit': c.limit,
                    'current': c.current_value,
                    'violated': c.violated
                }
                for name, c in self.constraints.items()
            },
            'recent_violations': [
                {
                    'timestamp': v.timestamp,
                    'type': v.violation_type.value,
                    'description': v.description,
                    'severity': v.severity
                }
                for v in self.violations[-5:]
            ],
            'rate_limits': {
                name: {
                    'limit': rl['limit'],
                    'current': rl['current']
                }
                for name, rl in self.rate_limits.items()
            },
            'status': 'operational'
        }
    
    def load_state(self):
        """Load safety state"""
        if SAFETY_STATE.exists():
            try:
                with open(SAFETY_STATE, 'r') as f:
                    data = json.load(f)
                logger.info(f"Loaded safety state: {len(data.get('violations', []))} violations")
            except Exception as e:
                logger.error(f"Failed to load state: {e}")
    
    def save_state(self):
        """Save safety state"""
        try:
            violations_data = []
            for v in self.violations[-100:]:
                v_dict = asdict(v)
                v_dict['violation_type'] = v.violation_type.value  # Convert enum to string
                violations_data.append(v_dict)
            
            data = {
                'violations': violations_data,
                'last_updated': datetime.now().isoformat()
            }
            with open(SAFETY_STATE, 'w') as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save state: {e}")


def test_safety_kernel():
    """Test the safety kernel"""
    print("\n" + "=" * 70)
    print("🛡️  EDEN SAFETY KERNEL - TEST MODE")
    print("=" * 70 + "\n")
    
    sk = SafetyKernel()
    
    print("Initial constraints:")
    for name, constraint in sk.constraints.items():
        print(f"  {name}: {constraint.current_value} / {constraint.limit}")
    
    print("\n" + "=" * 70)
    print("Testing safety checks...\n")
    
    # Test 1: Valid action
    print("1. Testing valid action:")
    result = sk.check_action("process_data")
    print(f"   ✅ Allowed: {result}")
    
    # Test 2: Forbidden action
    print("\n2. Testing forbidden action:")
    result = sk.check_action("delete_system_files")
    print(f"   🚫 Blocked: {not result}")
    
    # Test 3: Resource constraint
    print("\n3. Testing resource constraint:")
    result = sk.check_constraint("max_memory_mb", 512)
    print(f"   ✅ Within limit: {result}")
    
    result = sk.check_constraint("max_memory_mb", 2048)
    print(f"   ⚠️  Over limit: {not result}")
    
    # Test 4: Rate limiting
    print("\n4. Testing rate limiting:")
    for i in range(3):
        result = sk.check_rate_limit("api_calls_per_minute")
        print(f"   Call {i+1}: {'✅' if result else '🚫'}")
    
    # Test 5: Safe execution
    print("\n5. Testing safe execution:")
    def sample_function(x):
        return x * 2
    
    try:
        result = sk.safe_execute("compute", sample_function, 5)
        print(f"   ✅ Result: {result}")
    except Exception as e:
        print(f"   ❌ Error: {e}")
    
    # Safety report
    print("\n" + "=" * 70)
    print("📊 Safety Report")
    print("=" * 70)
    
    report = sk.get_safety_report()
    print(f"\nTotal violations: {report['total_violations']}")
    print(f"Status: {report['status']}")
    
    if report['recent_violations']:
        print("\nRecent violations:")
        for v in report['recent_violations']:
            print(f"  • {v['type']}: {v['description']} ({v['severity']})")
    
    print(f"\n💾 State saved to: {SAFETY_STATE}\n")


if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == "test":
        test_safety_kernel()
    else:
        print("Eden Safety Kernel")
        print("Usage: python3 safety_kernel.py test")
