"""Simple publish/subscribe event bus for AYN Antivirus.

Decouples the scan engine from consumers like the CLI, logger, quarantine
manager, and real-time monitor so each component can react to events
independently.
"""

from __future__ import annotations

import logging
import threading
from enum import Enum, auto
from typing import Any, Callable, Dict, List

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# Event types
# ---------------------------------------------------------------------------
class EventType(Enum):
    """All events emitted by the AYN engine."""

    THREAT_FOUND = auto()
    SCAN_STARTED = auto()
    SCAN_COMPLETED = auto()
    FILE_SCANNED = auto()
    SIGNATURE_UPDATED = auto()
    QUARANTINE_ACTION = auto()
    REMEDIATION_ACTION = auto()
    DASHBOARD_METRIC = auto()


# Type alias for subscriber callbacks.
Callback = Callable[[EventType, Any], None]


# ---------------------------------------------------------------------------
# EventBus
# ---------------------------------------------------------------------------
class EventBus:
    """Thread-safe publish/subscribe event bus.

    Usage::

        bus = EventBus()
        bus.subscribe(EventType.THREAT_FOUND, lambda et, data: print(data))
        bus.publish(EventType.THREAT_FOUND, {"path": "/tmp/evil.elf"})
    """

    def __init__(self) -> None:
        self._subscribers: Dict[EventType, List[Callback]] = {et: [] for et in EventType}
        self._lock = threading.Lock()

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------
    def subscribe(self, event_type: EventType, callback: Callback) -> None:
        """Register *callback* to be invoked whenever *event_type* is published.

        Parameters
        ----------
        event_type:
            The event to listen for.
        callback:
            A callable with signature ``(event_type, data) -> None``.
        """
        with self._lock:
            if callback not in self._subscribers[event_type]:
                self._subscribers[event_type].append(callback)

    def unsubscribe(self, event_type: EventType, callback: Callback) -> None:
        """Remove a previously-registered callback."""
        with self._lock:
            try:
                self._subscribers[event_type].remove(callback)
            except ValueError:
                pass

    def publish(self, event_type: EventType, data: Any = None) -> None:
        """Emit an event, invoking all registered callbacks synchronously.

        Exceptions raised by individual callbacks are logged and swallowed so
        that one faulty subscriber cannot break the pipeline.

        Parameters
        ----------
        event_type:
            The event being emitted.
        data:
            Arbitrary payload — typically a dataclass or dict.
        """
        with self._lock:
            callbacks = list(self._subscribers[event_type])

        for cb in callbacks:
            try:
                cb(event_type, data)
            except Exception:
                logger.exception(
                    "Subscriber %r raised an exception for event %s",
                    cb,
                    event_type.name,
                )

    def clear(self, event_type: EventType | None = None) -> None:
        """Remove all subscribers for *event_type*, or all subscribers if ``None``."""
        with self._lock:
            if event_type is None:
                for et in EventType:
                    self._subscribers[et].clear()
            else:
                self._subscribers[event_type].clear()


# ---------------------------------------------------------------------------
# Module-level singleton
# ---------------------------------------------------------------------------
event_bus = EventBus()
