"""Event bus for Orxaq system events -- stdlib only.

Provides pub/sub event routing for connecting components to
dashboards and monitoring systems.
"""

from __future__ import annotations

import threading
import time
from collections import defaultdict
from collections.abc import Callable
from dataclasses import dataclass, field
from enum import Enum
from typing import Any


class EventPriority(Enum):
    """Priority levels for events."""

    LOW = 0
    NORMAL = 1
    HIGH = 2
    CRITICAL = 3


@dataclass(frozen=True)
class Event:
    """Immutable event record."""

    topic: str
    data: dict[str, Any]
    priority: EventPriority = EventPriority.NORMAL
    timestamp: float = field(default_factory=time.time)
    source: str = ""

    def to_dict(self) -> dict[str, Any]:
        """Serialize to a JSON-compatible dict."""
        return {
            "topic": self.topic,
            "data": self.data,
            "priority": self.priority.value,
            "timestamp": self.timestamp,
            "source": self.source,
        }


class EventBus:
    """Thread-safe pub/sub event bus."""

    def __init__(self, max_history: int = 200) -> None:
        self._subscribers: dict[str, list[Callable[[Event], None]]] = defaultdict(list)
        self._history: list[Event] = []
        self._max_history = max_history
        self._lock = threading.Lock()

    def subscribe(self, topic: str, callback: Callable[[Event], None]) -> None:
        """Subscribe a callback to a topic."""
        with self._lock:
            self._subscribers[topic].append(callback)

    def unsubscribe(self, topic: str, callback: Callable[[Event], None]) -> None:
        """Remove a callback from a topic."""
        with self._lock:
            self._subscribers[topic] = [cb for cb in self._subscribers[topic] if cb is not callback]

    def publish(
        self,
        topic: str,
        data: dict[str, Any] | None = None,
        priority: EventPriority = EventPriority.NORMAL,
        source: str = "",
    ) -> Event:
        """Publish an event to all subscribers of the topic and wildcard."""
        event = Event(
            topic=topic,
            data=data or {},
            priority=priority,
            source=source,
        )
        with self._lock:
            self._history.append(event)
            if len(self._history) > self._max_history:
                self._history = self._history[-self._max_history :]
            callbacks = list(self._subscribers.get(topic, []))
            # Also notify wildcard subscribers
            callbacks.extend(self._subscribers.get("*", []))

        for cb in callbacks:
            try:
                cb(event)
            except Exception:  # noqa: BLE001
                pass
        return event

    def history(self, topic: str | None = None, limit: int = 50) -> list[Event]:
        """Return recent events, optionally filtered by topic."""
        with self._lock:
            events = list(self._history)
        if topic:
            events = [e for e in events if e.topic == topic]
        return events[-limit:]

    def topics(self) -> list[str]:
        """Return sorted list of topics with subscribers."""
        with self._lock:
            return sorted(self._subscribers.keys())

    def clear_history(self) -> None:
        """Remove all events from history."""
        with self._lock:
            self._history.clear()
