"""SQLAlchemy repository for Webhook entities."""


from __future__ import annotations

from collections.abc import Sequence
from uuid import UUID

from sqlalchemy import select

from noteflow.domain.webhooks import WebhookConfig, WebhookDelivery
from noteflow.infrastructure.converters.webhook_converters import WebhookConverter
from noteflow.infrastructure.persistence.models.integrations.webhook import (
    WebhookConfigModel,
    WebhookDeliveryModel,
)
from noteflow.infrastructure.persistence.repositories._base import (
    BaseRepository,
    DeleteByIdMixin,
    GetByIdMixin,
)


class SqlAlchemyWebhookRepository(
    BaseRepository,
    GetByIdMixin[WebhookConfigModel, WebhookConfig],
    DeleteByIdMixin[WebhookConfigModel],
):
    """SQLAlchemy implementation of WebhookRepository.

    Manages webhook configurations and delivery history for event notifications.
    Uses mixins for standardized get_by_id and delete operations.
    """

    _model_class: type[WebhookConfigModel] = WebhookConfigModel

    def _to_domain(self, model: WebhookConfigModel) -> WebhookConfig:
        """Convert ORM model to domain entity."""
        return WebhookConverter.config_to_domain(model)

    async def get_all_enabled(
        self,
        workspace_id: UUID | None = None,
    ) -> Sequence[WebhookConfig]:
        """Retrieve all enabled webhook configurations.

        Args:
            workspace_id: Optional filter by workspace.

        Returns:
            Sequence of enabled webhook configurations.
        """
        stmt = select(WebhookConfigModel).where(WebhookConfigModel.enabled.is_(True))
        if workspace_id is not None:
            stmt = stmt.where(WebhookConfigModel.workspace_id == workspace_id)
        stmt = stmt.order_by(WebhookConfigModel.created_at.desc())

        models = await self._execute_scalars(stmt)
        return [WebhookConverter.config_to_domain(m) for m in models]

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

        Args:
            workspace_id: Optional filter by workspace.

        Returns:
            Sequence of all webhook configurations.
        """
        stmt = select(WebhookConfigModel)
        if workspace_id is not None:
            stmt = stmt.where(WebhookConfigModel.workspace_id == workspace_id)
        stmt = stmt.order_by(WebhookConfigModel.created_at.desc())

        models = await self._execute_scalars(stmt)
        return [WebhookConverter.config_to_domain(m) for m in models]

    async def get_by_id(self, webhook_id: UUID) -> WebhookConfig | None:
        """Retrieve a webhook configuration by ID.

        Args:
            webhook_id: Unique webhook identifier.

        Returns:
            Webhook configuration or None if not found.
        """
        return await self._mixin_get_by_id(webhook_id)

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

        Args:
            config: Webhook configuration to create.

        Returns:
            Created webhook with any generated fields populated.
        """
        kwargs = WebhookConverter.config_to_orm_kwargs(config)
        model = WebhookConfigModel(**kwargs)
        await self._add_and_flush(model)
        return WebhookConverter.config_to_domain(model)

    async def update(self, config: WebhookConfig) -> WebhookConfig:
        """Update an existing webhook configuration.

        Args:
            config: Webhook configuration with updated values.

        Returns:
            Updated webhook configuration.

        Raises:
            ValueError: If webhook does not exist.
        """
        stmt = select(WebhookConfigModel).where(WebhookConfigModel.id == config.id)
        model = await self._execute_scalar(stmt)
        if not model:
            msg = f"Webhook {config.id} not found"
            raise ValueError(msg)

        # Update fields
        model.name = config.name
        model.url = config.url
        model.events = [e.value for e in config.events]
        model.secret = config.secret
        model.enabled = config.enabled
        model.timeout_ms = config.timeout_ms
        model.max_retries = config.max_retries

        await self._session.flush()
        return WebhookConverter.config_to_domain(model)

    async def delete(self, webhook_id: UUID) -> bool:
        """Delete a webhook configuration.

        Args:
            webhook_id: Unique webhook identifier.

        Returns:
            True if deleted, False if not found.
        """
        return await self._mixin_delete_by_id(webhook_id)

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

        Args:
            delivery: Delivery record to persist.

        Returns:
            Persisted delivery record.
        """
        kwargs = WebhookConverter.delivery_to_orm_kwargs(delivery)
        model = WebhookDeliveryModel(**kwargs)
        await self._add_and_flush(model)
        return WebhookConverter.delivery_to_domain(model)

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

        Args:
            webhook_id: Webhook to get deliveries for.
            limit: Maximum number of records.

        Returns:
            Sequence of delivery records, newest first.
        """
        stmt = (
            select(WebhookDeliveryModel)
            .where(WebhookDeliveryModel.webhook_id == webhook_id)
            .order_by(WebhookDeliveryModel.delivered_at.desc())
            .limit(limit)
        )
        models = await self._execute_scalars(stmt)
        return [WebhookConverter.delivery_to_domain(m) for m in models]
