"""Requirement helpers for gRPC service mixins.

Provides functions to verify required features and services are available,
aborting if preconditions are not met.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol

from noteflow.config.constants import FEATURE_NAME_PROJECTS

from ._abort import AbortableContext, abort_database_required, abort_failed_precondition

if TYPE_CHECKING:
    from noteflow.application.services.ner_service import NerService
    from noteflow.application.services.project_service import ProjectService


class SupportsProjects(Protocol):
    """Minimal protocol for project feature availability checks."""

    @property
    def supports_projects(self) -> bool: ...


class SupportsWebhooks(Protocol):
    """Minimal protocol for webhook feature availability checks."""

    @property
    def supports_webhooks(self) -> bool: ...


class SupportsEntities(Protocol):
    """Minimal protocol for entity feature availability checks."""

    @property
    def supports_entities(self) -> bool: ...


class SupportsIntegrations(Protocol):
    """Minimal protocol for integration feature availability checks."""

    @property
    def supports_integrations(self) -> bool: ...


class SupportsWorkspaces(Protocol):
    """Minimal protocol for workspace feature availability checks."""

    @property
    def supports_workspaces(self) -> bool: ...

# Feature names for abort_database_required calls
FEATURE_WEBHOOKS = "Webhooks"
FEATURE_ENTITIES = "Named Entities"
FEATURE_INTEGRATIONS = "Integrations"
FEATURE_WORKSPACES = "Workspaces"


# =============================================================================
# Feature Requirement Helpers
# =============================================================================


async def require_feature_projects(
    uow: SupportsProjects,
    context: AbortableContext,
) -> None:
    """Ensure projects feature is available, abort if not.

    Args:
        uow: Unit of work to check for project support.
        context: gRPC servicer context for abort.

    Raises:
        grpc.RpcError: UNIMPLEMENTED if projects not supported.
    """
    if not uow.supports_projects:
        await abort_database_required(context, FEATURE_NAME_PROJECTS)


async def require_feature_webhooks(
    uow: SupportsWebhooks,
    context: AbortableContext,
) -> None:
    """Ensure webhooks feature is available, abort if not.

    Args:
        uow: Unit of work to check for webhook support.
        context: gRPC servicer context for abort.

    Raises:
        grpc.RpcError: UNIMPLEMENTED if webhooks not supported.
    """
    if not uow.supports_webhooks:
        await abort_database_required(context, FEATURE_WEBHOOKS)


async def require_feature_entities(
    uow: SupportsEntities,
    context: AbortableContext,
) -> None:
    """Ensure named entities feature is available, abort if not.

    Args:
        uow: Unit of work to check for entity support.
        context: gRPC servicer context for abort.

    Raises:
        grpc.RpcError: UNIMPLEMENTED if entities not supported.
    """
    if not uow.supports_entities:
        await abort_database_required(context, FEATURE_ENTITIES)


async def require_feature_integrations(
    uow: SupportsIntegrations,
    context: AbortableContext,
) -> None:
    """Ensure integrations feature is available, abort if not.

    Args:
        uow: Unit of work to check for integration support.
        context: gRPC servicer context for abort.

    Raises:
        grpc.RpcError: UNIMPLEMENTED if integrations not supported.
    """
    if not uow.supports_integrations:
        await abort_database_required(context, FEATURE_INTEGRATIONS)


async def require_feature_workspaces(
    uow: SupportsWorkspaces,
    context: AbortableContext,
) -> None:
    """Ensure workspaces feature is available, abort if not.

    Args:
        uow: Unit of work to check for workspace support.
        context: gRPC servicer context for abort.

    Raises:
        grpc.RpcError: UNIMPLEMENTED if workspaces not supported.
    """
    if not uow.supports_workspaces:
        await abort_database_required(context, FEATURE_WORKSPACES)


# =============================================================================
# Service Requirement Helpers
# =============================================================================


async def require_project_service(
    project_service: ProjectService | None,
    context: AbortableContext,
) -> ProjectService:
    """Ensure project service is configured, abort if not.

    Args:
        project_service: Potentially null project service.
        context: gRPC servicer context for abort.

    Returns:
        The project service if configured.

    Raises:
        grpc.RpcError: FAILED_PRECONDITION if service not configured.
    """
    if project_service is None:
        await abort_failed_precondition(context, "Project service not configured")
    assert project_service is not None  # Type narrowing: abort never returns
    return project_service


async def require_ner_service(
    ner_service: NerService | None,
    context: AbortableContext,
) -> NerService:
    """Ensure NER service is configured, abort if not.

    Args:
        ner_service: Potentially null NER service.
        context: gRPC servicer context for abort.

    Returns:
        The NER service if configured.

    Raises:
        grpc.RpcError: FAILED_PRECONDITION if service not configured.
    """
    if ner_service is None:
        await abort_failed_precondition(
            context,
            "NER service not configured. Set NOTEFLOW_FEATURE_NER_ENABLED=true",
        )
    assert ner_service is not None  # Type narrowing: abort never returns
    return ner_service
