import asyncio
from abc import ABC, abstractmethod
from uuid import UUID

from openhands.app_server.event_callback.event_callback_models import (
    CreateEventCallbackRequest,
    EventCallback,
    EventCallbackPage,
    EventKind,
)
from openhands.app_server.services.injector import Injector
from openhands.sdk import Event
from openhands.sdk.utils.models import DiscriminatedUnionMixin


class EventCallbackService(ABC):
    """CRUD service for managing event callbacks."""

    @abstractmethod
    async def create_event_callback(
        self, request: CreateEventCallbackRequest
    ) -> EventCallback:
        """Create a new event callback."""

    @abstractmethod
    async def get_event_callback(self, id: UUID) -> EventCallback | None:
        """Get a single event callback, returning None if not found."""

    @abstractmethod
    async def delete_event_callback(self, id: UUID) -> bool:
        """Delete a event callback, returning True if deleted, False if not found."""

    @abstractmethod
    async def search_event_callbacks(
        self,
        conversation_id__eq: UUID | None = None,
        event_kind__eq: EventKind | None = None,
        event_id__eq: UUID | None = None,
        page_id: str | None = None,
        limit: int = 100,
    ) -> EventCallbackPage:
        """Search for event callbacks, optionally filtered by event_id."""

    async def batch_get_event_callbacks(
        self, event_callback_ids: list[UUID]
    ) -> list[EventCallback | None]:
        """Get a batch of event callbacks, returning None for any not found."""
        results = await asyncio.gather(
            *[
                self.get_event_callback(event_callback_id)
                for event_callback_id in event_callback_ids
            ]
        )
        return results

    @abstractmethod
    async def execute_callbacks(self, conversation_id: UUID, event: Event) -> None:
        """Execute any applicable callbacks for the event and store the results."""


class EventCallbackServiceInjector(
    DiscriminatedUnionMixin, Injector[EventCallbackService], ABC
):
    pass
