"""
Standardized Event Envelope.

All events in the system are wrapped in a standard envelope
that includes metadata, versioning, and tracing information.
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Dict, Optional
from uuid import uuid4


@dataclass
class EventEnvelope:
    """
    Standard event envelope for all system events.
    
    Ensures consistent event structure across all modules with
    built-in versioning, correlation, and tracing support.
    
    Example:
        event = EventEnvelope(
            event_type='plm.part.created',
            schema_version='1.0.0',
            tenant_id='tenant-123',
            payload={'part_id': 'uuid', 'name': 'Widget A'},
            metadata={'user_id': 'user-456', 'source_module': 'plm'},
        )
    """
    # Event identification
    event_id: str = field(default_factory=lambda: str(uuid4()))
    event_type: str = ''
    schema_version: str = '1.0.0'
    
    # Timing
    timestamp: str = field(
        default_factory=lambda: datetime.utcnow().isoformat() + 'Z'
    )
    
    # Multi-tenancy
    tenant_id: Optional[str] = None
    
    # Tracing
    correlation_id: Optional[str] = None
    causation_id: Optional[str] = None
    
    # Payload
    payload: Dict[str, Any] = field(default_factory=dict)
    
    # Metadata
    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        """Serialize to dictionary."""
        return {
            'event_id': self.event_id,
            'event_type': self.event_type,
            'schema_version': self.schema_version,
            'timestamp': self.timestamp,
            'tenant_id': self.tenant_id,
            'correlation_id': self.correlation_id,
            'causation_id': self.causation_id,
            'payload': self.payload,
            'metadata': self.metadata,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'EventEnvelope':
        """Deserialize from dictionary."""
        return cls(
            event_id=data.get('event_id', str(uuid4())),
            event_type=data.get('event_type', ''),
            schema_version=data.get('schema_version', '1.0.0'),
            timestamp=data.get('timestamp', datetime.utcnow().isoformat() + 'Z'),
            tenant_id=data.get('tenant_id'),
            correlation_id=data.get('correlation_id'),
            causation_id=data.get('causation_id'),
            payload=data.get('payload', {}),
            metadata=data.get('metadata', {}),
        )

    def with_correlation(self, correlation_id: str) -> 'EventEnvelope':
        """Return a copy with correlation_id set."""
        self.correlation_id = correlation_id
        return self

    def with_causation(self, causation_id: str) -> 'EventEnvelope':
        """Return a copy with causation_id (parent event) set."""
        self.causation_id = causation_id
        return self


def create_event(
    event_type: str,
    payload: Dict[str, Any],
    tenant_id: Optional[str] = None,
    user_id: Optional[str] = None,
    source_module: Optional[str] = None,
    schema_version: str = '1.0.0',
    correlation_id: Optional[str] = None,
    causation_id: Optional[str] = None,
    environment: Optional[str] = None,
) -> EventEnvelope:
    """
    Helper function to create a standardized event.
    
    Args:
        event_type: Dot-notated event type (e.g., 'plm.part.created')
        payload: Event-specific data
        tenant_id: Tenant identifier
        user_id: User who triggered the event
        source_module: Module that generated the event
        schema_version: Schema version (semver)
        correlation_id: Correlation ID for tracing
        causation_id: Parent event ID
        environment: Environment name
        
    Returns:
        EventEnvelope instance
    """
    metadata = {}
    if user_id:
        metadata['user_id'] = user_id
    if source_module:
        metadata['source_module'] = source_module
    if environment:
        metadata['environment'] = environment

    return EventEnvelope(
        event_type=event_type,
        schema_version=schema_version,
        tenant_id=tenant_id,
        correlation_id=correlation_id,
        causation_id=causation_id,
        payload=payload,
        metadata=metadata,
    )
