"""
Event Bus - Typed event system for internal communication.
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Callable, Dict, Generic, List, Optional, Type, TypeVar
from uuid import uuid4

import structlog

logger = structlog.get_logger()

T = TypeVar('T', bound='BaseEvent')


@dataclass
class BaseEvent:
    """Base class for all events."""
    event_id: str = field(default_factory=lambda: str(uuid4()))
    timestamp: datetime = field(default_factory=datetime.utcnow)
    tenant_id: Optional[str] = None
    user_id: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    @property
    def event_type(self) -> str:
        return f'{self.__class__.__module__}.{self.__class__.__name__}'


@dataclass
class DomainEvent(BaseEvent):
    """Domain event for business logic events."""
    aggregate_id: Optional[str] = None
    aggregate_type: Optional[str] = None


@dataclass
class IntegrationEvent(BaseEvent):
    """Integration event for cross-module communication."""
    source_module: Optional[str] = None
    target_module: Optional[str] = None


class EventHandler(Generic[T]):
    """Base event handler."""
    
    def handle(self, event: T) -> None:
        raise NotImplementedError


class EventBus:
    """
    In-memory event bus for synchronous event handling.
    For async, use Celery tasks.
    """
    _instance: Optional['EventBus'] = None
    _handlers: Dict[str, List[Callable]] = {}
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._handlers = {}
        return cls._instance
    
    def subscribe(self, event_type: Type[BaseEvent], handler: Callable[[BaseEvent], None]) -> None:
        """Subscribe a handler to an event type."""
        event_name = f'{event_type.__module__}.{event_type.__name__}'
        if event_name not in self._handlers:
            self._handlers[event_name] = []
        self._handlers[event_name].append(handler)
        logger.info('event_handler_subscribed', event_type=event_name)
    
    def unsubscribe(self, event_type: Type[BaseEvent], handler: Callable[[BaseEvent], None]) -> None:
        """Unsubscribe a handler from an event type."""
        event_name = f'{event_type.__module__}.{event_type.__name__}'
        if event_name in self._handlers:
            self._handlers[event_name].remove(handler)
    
    def publish(self, event: BaseEvent) -> None:
        """Publish an event to all subscribed handlers."""
        event_name = event.event_type
        logger.info('event_published', event_type=event_name, event_id=event.event_id)
        
        handlers = self._handlers.get(event_name, [])
        for handler in handlers:
            try:
                handler(event)
            except Exception as e:
                logger.error(
                    'event_handler_error',
                    event_type=event_name,
                    event_id=event.event_id,
                    error=str(e)
                )
    
    def publish_async(self, event: BaseEvent) -> None:
        """Publish an event asynchronously via Celery."""
        from .tasks import process_event_async
        process_event_async.delay(event)


# Global event bus instance
event_bus = EventBus()


def subscribe(event_type: Type[BaseEvent]):
    """Decorator to subscribe a function to an event type."""
    def decorator(func: Callable[[BaseEvent], None]):
        event_bus.subscribe(event_type, func)
        return func
    return decorator
