"""Streaming event bus for broadcasting model events.

Provides a central event bus that services use to publish CRUD events.
Subscribers (GraphQL subscriptions, SSE endpoints, WebSocket handlers)
receive events through async generators.

⚠️ AUTO-GENERATED BY PRISM - DO NOT EDIT
"""

from __future__ import annotations

import asyncio
import logging
from collections.abc import AsyncGenerator
from dataclasses import dataclass, field
from enum import StrEnum
from typing import Any

logger = logging.getLogger(__name__)


class EventType(StrEnum):
    """Standard CRUD event types."""

    CREATED = "created"
    UPDATED = "updated"
    DELETED = "deleted"
{% for event in custom_events %}
    {{ event | upper }} = "{{ event }}"
{% endfor %}


@dataclass
class StreamEvent:
    """A streaming event emitted by services.

    Attributes:
        model: The model name (e.g., 'Customer').
        event_type: The event type (created, updated, deleted, or custom).
        entity_id: The entity ID.
        payload: The full entity data (if include_payload is enabled).
        metadata: Optional extra metadata.
    """

    model: str
    event_type: str
    entity_id: int | str
    payload: dict[str, Any] | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


class EventBus:
    """In-memory event bus using asyncio queues.

    Each subscriber gets its own queue. Events are broadcast to all
    active subscribers.

    Usage:
        bus = EventBus()

        # Subscribe to events
        async for event in bus.subscribe(model="Customer", event_types=["created"]):
            print(f"New customer: {event.entity_id}")

        # Publish events (from services)
        await bus.publish(StreamEvent(
            model="Customer",
            event_type="created",
            entity_id=1,
            payload={"name": "Alice"},
        ))
    """

    def __init__(self) -> None:
        self._subscribers: list[asyncio.Queue[StreamEvent]] = []
        self._lock = asyncio.Lock()

    async def publish(self, event: StreamEvent) -> None:
        """Publish an event to all subscribers.

        Args:
            event: The event to broadcast.
        """
        async with self._lock:
            dead_queues: list[asyncio.Queue[StreamEvent]] = []
            for queue in self._subscribers:
                try:
                    queue.put_nowait(event)
                except asyncio.QueueFull:
                    logger.warning(
                        "Subscriber queue full, dropping event for %s.%s",
                        event.model,
                        event.event_type,
                    )
                    dead_queues.append(queue)
            # Remove dead queues
            for q in dead_queues:
                self._subscribers.remove(q)

    async def subscribe(
        self,
        *,
        model: str | None = None,
        event_types: list[str] | None = None,
        max_queue_size: int = 256,
    ) -> AsyncGenerator[StreamEvent, None]:
        """Subscribe to events with optional filtering.

        Args:
            model: Filter to events for this model name only.
            event_types: Filter to these event types only.
            max_queue_size: Maximum queue size per subscriber.

        Yields:
            StreamEvent objects matching the filter criteria.
        """
        queue: asyncio.Queue[StreamEvent] = asyncio.Queue(maxsize=max_queue_size)
        async with self._lock:
            self._subscribers.append(queue)
        try:
            while True:
                event = await queue.get()
                if model and event.model != model:
                    continue
                if event_types and event.event_type not in event_types:
                    continue
                yield event
        finally:
            async with self._lock:
                if queue in self._subscribers:
                    self._subscribers.remove(queue)


# Global event bus instance
event_bus = EventBus()


__all__ = ["EventBus", "EventType", "StreamEvent", "event_bus"]
