"""Repository protocols for external service entities.

Contains Entity (NER), Integration, and Webhook repository protocols.
"""

from __future__ import annotations

from collections.abc import Sequence
from typing import TYPE_CHECKING, Protocol

if TYPE_CHECKING:
    from uuid import UUID

    from noteflow.application.observability.ports import UsageEvent
    from noteflow.domain.entities import Integration, SyncRun
    from noteflow.domain.entities.named_entity import NamedEntity
    from noteflow.domain.value_objects import MeetingId
    from noteflow.domain.webhooks import WebhookConfig, WebhookDelivery


class EntityRepository(Protocol):
    """Repository protocol for NamedEntity operations (NER results)."""

    async def save(self, entity: NamedEntity) -> NamedEntity:
        """Save or update a named entity.

        Args:
            entity: Entity to save.

        Returns:
            Saved entity with db_id populated.
        """
        ...

    async def save_batch(
        self, entities: Sequence[NamedEntity],
    ) -> Sequence[NamedEntity]:
        """Save multiple entities efficiently.

        Args:
            entities: Entities to save.

        Returns:
            Saved entities with db_ids populated.
        """
        ...

    async def get(self, entity_id: UUID) -> NamedEntity | None:
        """Get entity by ID.

        Args:
            entity_id: Entity UUID.

        Returns:
            Entity if found, None otherwise.
        """
        ...

    async def get_by_meeting(self, meeting_id: MeetingId) -> Sequence[NamedEntity]:
        """Get all entities for a meeting.

        Args:
            meeting_id: Meeting UUID.

        Returns:
            List of entities.
        """
        ...

    async def delete_by_meeting(self, meeting_id: MeetingId) -> int:
        """Delete all entities for a meeting.

        Args:
            meeting_id: Meeting UUID.

        Returns:
            Number of deleted entities.
        """
        ...

    async def update_pinned(self, entity_id: UUID, is_pinned: bool) -> bool:
        """Update the pinned status of an entity.

        Args:
            entity_id: Entity UUID.
            is_pinned: New pinned status.

        Returns:
            True if entity was found and updated.
        """
        ...

    async def exists_for_meeting(self, meeting_id: MeetingId) -> bool:
        """Check if any entities exist for a meeting.

        Args:
            meeting_id: Meeting UUID.

        Returns:
            True if at least one entity exists.
        """
        ...

    async def update(
        self,
        entity_id: UUID,
        text: str | None = None,
        category: str | None = None,
    ) -> NamedEntity | None:
        """Update an existing entity.

        Args:
            entity_id: Entity UUID.
            text: New text value (optional).
            category: New category value (optional).

        Returns:
            Updated entity if found, None otherwise.
        """
        ...

    async def delete(self, entity_id: UUID) -> bool:
        """Delete an entity by ID.

        Args:
            entity_id: Entity UUID.

        Returns:
            True if entity was found and deleted.
        """
        ...


class IntegrationRepository(Protocol):
    """Repository protocol for external service integrations.

    Manages OAuth-connected services like calendars, email providers, and PKM tools.
    """

    async def get(self, integration_id: UUID) -> Integration | None:
        """Retrieve an integration by ID.

        Args:
            integration_id: Integration UUID.

        Returns:
            Integration if found, None otherwise.
        """
        ...

    async def get_by_provider(
        self,
        provider: str,
        integration_type: str | None = None,
    ) -> Integration | None:
        """Retrieve an integration by provider name.

        Args:
            provider: Provider name (e.g., 'google', 'outlook').
            integration_type: Optional type filter.

        Returns:
            Integration if found, None otherwise.
        """
        ...

    async def create(self, integration: Integration) -> Integration:
        """Persist a new integration.

        Args:
            integration: Integration to create.

        Returns:
            Created integration.
        """
        ...

    async def update(self, integration: Integration) -> Integration:
        """Update an existing integration.

        Args:
            integration: Integration with updated fields.

        Returns:
            Updated integration.

        Raises:
            ValueError: If integration does not exist.
        """
        ...

    async def delete(self, integration_id: UUID) -> bool:
        """Delete an integration and its secrets.

        Args:
            integration_id: Integration UUID.

        Returns:
            True if deleted, False if not found.
        """
        ...

    async def get_secrets(self, integration_id: UUID) -> dict[str, str] | None:
        """Get encrypted secrets for an integration.

        Args:
            integration_id: Integration UUID.

        Returns:
            Dictionary of secret key-value pairs, or None if not found.
        """
        ...

    async def set_secrets(
        self, integration_id: UUID, secrets: dict[str, str],
    ) -> None:
        """Store encrypted secrets for an integration.

        Args:
            integration_id: Integration UUID.
            secrets: Dictionary of secret key-value pairs.
        """
        ...

    async def list_by_type(self, integration_type: str) -> Sequence[Integration]:
        """List integrations by type.

        Args:
            integration_type: Integration type (e.g., 'calendar', 'email').

        Returns:
            List of integrations of the specified type.
        """
        ...

    async def list_all(self) -> Sequence[Integration]:
        """List all integrations for the current workspace context.

        Returns:
            All integrations the user has access to.
        """
        ...

    # Sync run operations

    async def create_sync_run(self, sync_run: SyncRun) -> SyncRun:
        """Create a new sync run record.

        Args:
            sync_run: Sync run to persist.

        Returns:
            Created sync run.
        """
        ...

    async def get_sync_run(self, sync_run_id: UUID) -> SyncRun | None:
        """Retrieve a sync run by ID.

        Args:
            sync_run_id: Sync run UUID.

        Returns:
            SyncRun if found, None otherwise.
        """
        ...

    async def update_sync_run(self, sync_run: SyncRun) -> SyncRun:
        """Update an existing sync run.

        Args:
            sync_run: Sync run with updated fields.

        Returns:
            Updated sync run.
        """
        ...

    async def list_sync_runs(
        self,
        integration_id: UUID,
        limit: int = 20,
        offset: int = 0,
    ) -> tuple[Sequence[SyncRun], int]:
        """List sync runs for an integration with pagination.

        Args:
            integration_id: Integration to list runs for.
            limit: Maximum runs to return.
            offset: Pagination offset.

        Returns:
            Tuple of (sync runs newest first, total count).
        """
        ...


class WebhookRepository(Protocol):
    """Repository for webhook configuration and delivery operations."""

    async def get_all_enabled(
        self, workspace_id: UUID | None = None,
    ) -> Sequence[WebhookConfig]:
        """Return all enabled webhooks, optionally filtered by workspace."""
        ...

    async def get_all(
        self, workspace_id: UUID | None = None,
    ) -> Sequence[WebhookConfig]:
        """Return all webhooks regardless of enabled status."""
        ...

    async def get_by_id(self, webhook_id: UUID) -> WebhookConfig | None:
        """Return webhook by ID or None if not found."""
        ...

    async def create(self, config: WebhookConfig) -> WebhookConfig:
        """Persist a new webhook configuration."""
        ...

    async def update(self, config: WebhookConfig) -> WebhookConfig:
        """Update existing webhook. Raises ValueError if not found."""
        ...

    async def delete(self, webhook_id: UUID) -> bool:
        """Delete webhook by ID. Return True if deleted, False if not found."""
        ...

    async def add_delivery(self, delivery: WebhookDelivery) -> WebhookDelivery:
        """Record a webhook delivery attempt."""
        ...

    async def get_deliveries(
        self, webhook_id: UUID, limit: int = 50,
    ) -> Sequence[WebhookDelivery]:
        """Return delivery history for webhook, newest first."""
        ...


class UsageEventRepository(Protocol):
    """Repository for usage event persistence and aggregation.

    Tracks resource consumption for analytics, billing, and monitoring.
    """

    async def add(self, event: UsageEvent) -> UsageEvent:
        """Persist a usage event.

        Args:
            event: UsageEvent to persist.

        Returns:
            Persisted event.
        """
        ...

    async def add_batch(self, events: Sequence[UsageEvent]) -> int:
        """Persist multiple usage events efficiently.

        Args:
            events: UsageEvents to persist.

        Returns:
            Number of events persisted.
        """
        ...
