"""Event-driven framework core implementation."""
import threading
import time
import heapq
from typing import Callable, Dict, Any
from security.encrypt import encrypt_data, decrypt_data, AES256Cipher
from contextlib import contextmanager
class EventQueue:
    """Priority queue for event processing with thread safety."""
    
    def __init__(self):
        self._queue = []
        self._lock = threading.RLock()
        self._event = threading.Event()
        
    def push(self, priority: int, event: Dict[str, Any]) -> None:
        """Add event to queue with priority."""
        with self._lock:
            heapq.heappush(self._queue, (priority, time.time(), event))
            self._event.set()
            
    def pop(self) -> Dict[str, Any]:
        """Get highest priority event."""
        while True:
            with self._lock:
                if self._queue:
                    return heapq.heappop(self._queue)[2]
            self._event.wait()
            self._event.clear()

class EventDispatcher:
    """Core event routing and handling system."""
    
    def __init__(self, scheduler, worker_count=4, cipher_pool_size=4):
        self._handlers = {}
        self._queue = EventQueue()
        self._running = False
        self._scheduler = scheduler
        self._worker_threads = []
        self._worker_count = worker_count
        self._metrics = {
            'events_processed': 0,
            'errors': 0,
            'last_event_time': None
        }
        self._cipher_pool = CipherPool(
            size=cipher_pool_size,
            algorithm='AES-256'
        )
        
    def register_handler(self, event_type: str, handler: Callable) -> None:
        """Register event handler for specific event type."""
        with threading.RLock():
            if event_type not in self._handlers:
                self._handlers[event_type] = []
            self._handlers[event_type].append(handler)
            
    def dispatch(self, event: Dict[str, Any]) -> None:
        """Dispatch event to appropriate handlers."""
        event_type = event.get('type')
        if not event_type:
            return
            
        handlers = self._handlers.get(event_type, [])
        for handler in handlers:
            try:
                handler(event)
            except Exception as e:
                print(f"Error in event handler: {str(e)}")
                
    def start(self) -> None:
        """Start event processing loop."""
        if self._running:
            return
            
        self._running = True
        for i in range(self._worker_count):
            thread = threading.Thread(
                target=self._process_events,
                daemon=True,
                name=f"EventWorker-{i}"
            )
            thread.start()
            self._worker_threads.append(thread)
        
    def _process_events(self) -> None:
        """Main event processing loop."""
        while self._running:
            event = self._queue.pop()
            with threading.RLock():
                self._metrics['events_processed'] += 1
                self._metrics['last_event_time'] = time.time()
            try:
                with self._cipher_pool.get_cipher() as cipher:
                    encrypted_event = {
                        'type': event.get('type'),
                        'timestamp': time.time(),
                        'data': cipher.encrypt(event)
                    }
                    self.dispatch(encrypted_event)
            except Exception as e:
                with threading.RLock():
                    self._metrics['errors'] += 1
                    self._metrics['last_error'] = str(e)
            
    def stop(self) -> None:
        """Stop event processing."""
        self._running = False
        for thread in self._worker_threads:
            thread.join()
            
    def schedule_event(self, event: Dict[str, Any], delay: float) -> None:
        """Schedule delayed event execution."""
        def delayed_dispatch():
            time.sleep(delay)
            self._queue.push(0, event)
            
        self._scheduler.register_task(
            f"delayed_{time.time()}",
            f"* * * * *",  # Will run immediately
            delayed_dispatch
        )

class EventSystem:
    """Main event system interface."""
    
    def __init__(self, scheduler):
        self.dispatcher = EventDispatcher(scheduler)
        self.encryption_enabled = True
        self._performance_stats = {
            'min_latency': float('inf'),
            'max_latency': 0,
            'avg_latency': 0,
            'total_events': 0
        }
        
    def publish(self, event: Dict[str, Any], priority: int = 0) -> None:
        """Publish event to system."""
        if self.encryption_enabled:
            event = {
                'encrypted': True,
                'data': encrypt_data(event)
            }
        start_time = time.time()
        self.dispatcher._queue.push(priority, event)
        latency = time.time() - start_time
        self._update_stats(latency)
        
    def subscribe(self, event_type: str, handler: Callable) -> None:
        """Subscribe to events of specific type."""
        if self.encryption_enabled:
            def wrapped_handler(event):
                if event.get('encrypted'):
                    try:
                        event = decrypt_data(event['data'])
                    except Exception as e:
                        print(f"Decryption error: {str(e)}")
                        return
                handler(event)
            self.dispatcher.register_handler(event_type, wrapped_handler)
        else:
            self.dispatcher.register_handler(event_type, handler)
            
    def _update_stats(self, latency):
        """Update performance statistics."""
        with threading.RLock():
            stats = self._performance_stats
            stats['total_events'] += 1
            stats['min_latency'] = min(stats['min_latency'], latency)
            stats['max_latency'] = max(stats['max_latency'], latency)
            stats['avg_latency'] = (
                (stats['avg_latency'] * (stats['total_events'] - 1) + latency)
                / stats['total_events']
            )
            
    def get_performance_stats(self):
        """Get current performance statistics."""
        with threading.RLock():
            return self._performance_stats.copy()