"""Base event classes and interfaces."""

import json
from abc import ABC, abstractmethod
from collections.abc import Awaitable, Callable
from datetime import UTC, datetime
from typing import Any

import ulid


# Each payload MUST include: `event_id (ulid)`, `occurred_at (iso)`, `actor`, `tenant_id`, `trace_id`, `schema_version`, and a `data` object (service-specific).
class EventPayload:
    """Standard event payload structure"""

    def __init__(  # pylint: disable=too-many-arguments,too-many-positional-arguments
        self,
        data: dict[str, Any],
        actor: str,
        tenant_id: str,
        trace_id: str | None = None,
        schema_version: str = "1.0",
    ):
        self.event_id = str(ulid.new())
        self.occurred_at = datetime.now(UTC).isoformat()
        self.actor = actor
        self.tenant_id = tenant_id
        self.trace_id = trace_id
        self.schema_version = schema_version
        self.data = data

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for serialization"""
        return {
            "event_id": self.event_id,
            "occurred_at": self.occurred_at,
            "actor": self.actor,
            "tenant_id": self.tenant_id,
            "trace_id": self.trace_id,
            "schema_version": self.schema_version,
            "data": self.data,
        }

    def to_json(self) -> str:
        """Convert to JSON string"""
        return json.dumps(self.to_dict())


class EventBus(ABC):
    """Abstract event bus interface"""

    @abstractmethod
    async def publish(self, topic: str, payload: EventPayload) -> bool:
        """Publish event to topic"""

    @abstractmethod
    async def subscribe(
        self, topic: str, handler: Callable[[str, EventPayload], Awaitable[None]]
    ) -> None:
        """Subscribe to topic with handler"""

    @abstractmethod
    async def start(self) -> None:
        """Start the event bus"""

    @abstractmethod
    async def stop(self) -> None:
        """Stop the event bus"""
