"""
Event models for Information Spaces.

This module defines the event types and data structures used
by the event system for space operations.
"""

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Dict, Optional
import uuid


class SpaceEventType(Enum):
    """
    Types of events that can occur in the space system.

    Events are organized by category:
    - Space lifecycle: creation, updates, deletion, status changes
    - Document operations: add, update, remove, move
    - Variable operations: set, delete
    - Rendering: start, complete, fail
    - Sync: start, complete, conflict
    """

    # Space lifecycle events
    SPACE_CREATED = "space.created"
    SPACE_UPDATED = "space.updated"
    SPACE_DELETED = "space.deleted"
    SPACE_ACTIVATED = "space.activated"
    SPACE_ARCHIVED = "space.archived"

    # Document events
    DOCUMENT_ADDED = "document.added"
    DOCUMENT_UPDATED = "document.updated"
    DOCUMENT_REMOVED = "document.removed"
    DOCUMENT_MOVED = "document.moved"
    DOCUMENT_CONTENT_CHANGED = "document.content_changed"

    # Variable events
    VARIABLE_SET = "variable.set"
    VARIABLE_DELETED = "variable.deleted"

    # Reference events
    REFERENCE_ADDED = "reference.added"
    REFERENCE_CLEARED = "reference.cleared"

    # Rendering events
    RENDER_STARTED = "render.started"
    RENDER_COMPLETED = "render.completed"
    RENDER_FAILED = "render.failed"

    # Sync events
    SYNC_STARTED = "sync.started"
    SYNC_COMPLETED = "sync.completed"
    SYNC_CONFLICT = "sync.conflict"

    # Cache events
    CACHE_INVALIDATED = "cache.invalidated"


@dataclass
class SpaceEvent:
    """
    Represents an event in the space system.

    Events are immutable records of operations that have occurred.
    They carry enough information for handlers to react appropriately.

    Attributes:
        event_type: The type of event
        space_id: The ID of the affected space
        payload: Event-specific data
        event_id: Unique identifier for this event
        timestamp: When the event occurred
        source: Optional identifier of the event source
        correlation_id: Optional ID to correlate related events
    """

    event_type: SpaceEventType
    space_id: str
    payload: Dict[str, Any] = field(default_factory=dict)
    event_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    timestamp: datetime = field(default_factory=datetime.now)
    source: Optional[str] = None
    correlation_id: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        """Convert the event to a dictionary for serialization."""
        return {
            "event_id": self.event_id,
            "event_type": self.event_type.value,
            "space_id": self.space_id,
            "payload": self.payload,
            "timestamp": self.timestamp.isoformat(),
            "source": self.source,
            "correlation_id": self.correlation_id,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "SpaceEvent":
        """Create an event from a dictionary."""
        return cls(
            event_id=data.get("event_id", str(uuid.uuid4())),
            event_type=SpaceEventType(data["event_type"]),
            space_id=data["space_id"],
            payload=data.get("payload", {}),
            timestamp=datetime.fromisoformat(data["timestamp"])
            if data.get("timestamp")
            else datetime.now(),
            source=data.get("source"),
            correlation_id=data.get("correlation_id"),
        )


# Convenience factory functions for common events


def space_created_event(
    space_id: str,
    name: str,
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a SPACE_CREATED event."""
    return SpaceEvent(
        event_type=SpaceEventType.SPACE_CREATED,
        space_id=space_id,
        payload={"name": name},
        source=source,
    )


def space_updated_event(
    space_id: str,
    changes: Dict[str, Any],
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a SPACE_UPDATED event."""
    return SpaceEvent(
        event_type=SpaceEventType.SPACE_UPDATED,
        space_id=space_id,
        payload={"changes": changes},
        source=source,
    )


def space_deleted_event(
    space_id: str,
    name: str,
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a SPACE_DELETED event."""
    return SpaceEvent(
        event_type=SpaceEventType.SPACE_DELETED,
        space_id=space_id,
        payload={"name": name},
        source=source,
    )


def document_added_event(
    space_id: str,
    document_id: str,
    space_path: str,
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a DOCUMENT_ADDED event."""
    return SpaceEvent(
        event_type=SpaceEventType.DOCUMENT_ADDED,
        space_id=space_id,
        payload={"document_id": document_id, "space_path": space_path},
        source=source,
    )


def document_updated_event(
    space_id: str,
    document_id: str,
    changes: Dict[str, Any],
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a DOCUMENT_UPDATED event."""
    return SpaceEvent(
        event_type=SpaceEventType.DOCUMENT_UPDATED,
        space_id=space_id,
        payload={"document_id": document_id, "changes": changes},
        source=source,
    )


def document_removed_event(
    space_id: str,
    document_id: str,
    space_path: str,
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a DOCUMENT_REMOVED event."""
    return SpaceEvent(
        event_type=SpaceEventType.DOCUMENT_REMOVED,
        space_id=space_id,
        payload={"document_id": document_id, "space_path": space_path},
        source=source,
    )


def document_content_changed_event(
    space_id: str,
    document_id: str,
    old_hash: Optional[str],
    new_hash: str,
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a DOCUMENT_CONTENT_CHANGED event."""
    return SpaceEvent(
        event_type=SpaceEventType.DOCUMENT_CONTENT_CHANGED,
        space_id=space_id,
        payload={
            "document_id": document_id,
            "old_hash": old_hash,
            "new_hash": new_hash,
        },
        source=source,
    )


def cache_invalidated_event(
    space_id: str,
    document_ids: list,
    reason: str,
    source: Optional[str] = None,
) -> SpaceEvent:
    """Create a CACHE_INVALIDATED event."""
    return SpaceEvent(
        event_type=SpaceEventType.CACHE_INVALIDATED,
        space_id=space_id,
        payload={"document_ids": document_ids, "reason": reason},
        source=source,
    )
