"""
Base Event Contract

Abstract base classes for domain events.
"""

from abc import ABC
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, Any, Dict
from uuid import UUID, uuid4


@dataclass
class BaseDomainEvent(ABC):
    """
    Abstract base class for all domain events.
    
    Domain events describe something that happened in the business domain.
    They are immutable and carry only necessary data.
    
    Rules:
    - Describe business significance
    - Carry only necessary data
    - No heavy logic
    - Handled by application layer
    """
    
    event_id: UUID = field(default_factory=uuid4)
    occurred_at: datetime = field(default_factory=datetime.utcnow)
    correlation_id: Optional[UUID] = None
    causation_id: Optional[UUID] = None
    
    @property
    def event_type(self) -> str:
        """Return the event type name."""
        return self.__class__.__name__
    
    def to_dict(self) -> Dict[str, Any]:
        """Serialize event to dictionary."""
        return {
            "event_id": str(self.event_id),
            "event_type": self.event_type,
            "occurred_at": self.occurred_at.isoformat(),
            "correlation_id": str(self.correlation_id) if self.correlation_id else None,
            "causation_id": str(self.causation_id) if self.causation_id else None,
            "payload": self._get_payload(),
        }
    
    def _get_payload(self) -> Dict[str, Any]:
        """Get event-specific payload. Override in subclasses."""
        return {}


@dataclass
class TenantDomainEvent(BaseDomainEvent):
    """Domain event with tenant context."""
    
    tenant_id: UUID = field(default_factory=uuid4)
    
    def _get_payload(self) -> Dict[str, Any]:
        return {"tenant_id": str(self.tenant_id)}


@dataclass
class UserDomainEvent(TenantDomainEvent):
    """Domain event with user context."""
    
    user_id: UUID = field(default_factory=uuid4)
    
    def _get_payload(self) -> Dict[str, Any]:
        payload = super()._get_payload()
        payload["user_id"] = str(self.user_id)
        return payload
