# backend/app/automation/event_bus.py
# event_bus 정본(단일 고정) — post-mvp-overview.md §5.
# `from app.automation.event_bus import bus` (bus.publish('x', {...}))
# `from app.automation.event_bus import publish` (publish('x', {...}), emit 동일)
# 둘 다 정상. app/events.py·app/event_bus.py 같은 다른 경로는 정본이 아니다.
from __future__ import annotations

from collections import defaultdict
from collections.abc import Callable
from dataclasses import dataclass, field
from datetime import UTC, datetime


@dataclass
class Event:
    type: str
    payload: dict = field(default_factory=dict)
    at: datetime = field(default_factory=lambda: datetime.now(UTC))


class EventBus:
    """in-process 동기 이벤트 버스. 핸들러는 (Event) -> None.
    프로토타입은 publish 즉시 동기 디스패치. phase-14 worker 가 비동기/스케줄로 확장."""

    def __init__(self) -> None:
        self._subs: dict[str, list[Callable[[Event], None]]] = defaultdict(list)
        self.history: list[Event] = []  # 테스트/디버그용 발행 기록

    def subscribe(self, event_type: str, handler: Callable[[Event], None]) -> None:
        self._subs[event_type].append(handler)

    def publish(self, event_type: str, payload: dict | None = None) -> Event:
        ev = Event(type=event_type, payload=payload or {})
        self.history.append(ev)
        for h in list(self._subs.get(event_type, [])):
            try:
                h(ev)  # 한 구독자의 예외가 다른 구독자를 막지 않도록 격리
            except Exception:
                pass
        return ev


# 전역 단일 버스(데모). 테스트는 새 인스턴스를 주입해 격리한다.
bus = EventBus()

# 모듈 레벨 편의 export — post-mvp-overview.md event_bus 정본.
publish = bus.publish  # publish('automation.matched', {...})
subscribe = bus.subscribe  # subscribe('automation.matched', handler)
emit = bus.publish  # emit 은 publish 의 별칭(동일 동작)
