import abc
from typing import Any

from typing_extensions import override

from ._base import NotchPayBaseResource, AsyncNotchPayBaseResource
from .exceptions import NotchPayError
from .schemas.base import MaybeAwaitable, NotchPayResponse
from .schemas.webhooks import (
    NotchPayWebhookCreate,
    NotchPayWebhookUpdate,
    NotchPayWebhookResponse, NotchPayWebhooksListResponse,
)

__all__ = [
    "NotchPayWebhooks",
    "AsyncNotchPayWebhooks",
]


class _NotchPayWebhooks(abc.ABC):
    """Abstract base class for Notchpay webhooks management."""

    @abc.abstractmethod
    def list(
            self,
            limit: int = 30,
            page: int = 1,
    ) -> MaybeAwaitable[NotchPayWebhooksListResponse]:
        """List all webhooks.

        Args:
            limit: Number of items per page (default: 30).
            page: Page number (default: 1).

        Returns:
            NotchPayWebhooksListResponse: Paginated list of webhooks.

        Raises:
            NotchPayError: In case of API request error.

        References:
            https://developer.notchpay.co/api-reference/list-all-webhooks

        Example:
            >>> from notchpay import NotchPay
            >>> notchpay = NotchPay("your_api_key")
            >>> webhooks = notchpay.webhooks.list(limit=10)
            >>> for webhook in webhooks.items:
            ...     print(webhook.id, webhook.url)
        """
        ...

    @abc.abstractmethod
    def create(self, data: NotchPayWebhookCreate) -> MaybeAwaitable[NotchPayWebhookResponse]:
        """Create a new webhook.

        Args:
            data: Webhook creation data.

        Returns:
            NotchPayWebhookResponse: Webhook creation response.

        Raises:
            NotchPayError: In case of creation error.

        References:
            https://developer.notchpay.co/api-reference/create-a-webhook

        Example:
            >>> from notchpay import NotchPay
            >>> notchpay = NotchPay("your_api_key")
            >>> webhook = notchpay.webhooks.create(
            ...     NotchPayWebhookCreate(
            ...         url="https://example.com/webhooks",
            ...         events=[WebhookEventType.PAYMENT_COMPLETE, WebhookEventType.PAYMENT_FAILED],
            ...         description="Payment notifications"
            ...     )
            ... )
            >>> print(webhook.endpoint.id)
        """
        ...

    @abc.abstractmethod
    def retrieve(self, webhook_id: str) -> MaybeAwaitable[NotchPayWebhookResponse]:
        """Retrieve webhook details.

        Args:
            webhook_id: Unique webhook identifier.

        Returns:
            NotchPayWebhookResponse: Webhook response.

        Raises:
            NotchPayError: If webhook doesn't exist or in case of API error.

        References:
            https://developer.notchpay.co/api-reference/retrieve-a-webhook

        Example:
            >>> from notchpay import NotchPay
            >>> notchpay = NotchPay("your_api_key")
            >>> webhook = notchpay.webhooks.retrieve("ep_test.ngsp7bSrFunksVxPo")
            >>> print(webhook.endpoint.url)
        """
        ...

    @abc.abstractmethod
    def update(self, webhook_id: str, data: NotchPayWebhookUpdate) -> MaybeAwaitable[NotchPayWebhookResponse]:
        """Update a webhook.

        Args:
            webhook_id: Unique webhook identifier.
            data: Webhook update data.

        Returns:
            NotchPayWebhookResponse: Update response.

        Raises:
            NotchPayError: If webhook doesn't exist or in case of API error.

        References:
            https://developer.notchpay.co/api-reference/update-a-webhook

        Example:
            >>> from notchpay import NotchPay
            >>> notchpay = NotchPay("your_api_key")
            >>> webhook = notchpay.webhooks.update(
            ...     "ep_test.ngsp7bSrFunksVxPo",
            ...     NotchPayWebhookUpdate(active=False)
            ... )
            >>> print(webhook.endpoint.status)
        """
        ...

    @abc.abstractmethod
    def delete(self, webhook_id: str) -> MaybeAwaitable[NotchPayResponse]:
        """Delete a webhook.

        Args:
            webhook_id: Unique identifier of the webhook to delete.

        Returns:
            NotchPayResponse: Deletion response.

        Raises:
            NotchPayError: If webhook cannot be deleted.

        References:
            https://developer.notchpay.co/api-reference/delete-a-webhook

        Example:
            >>> from notchpay import NotchPay
            >>> notchpay = NotchPay("your_api_key")
            >>> response = notchpay.webhooks.delete("ep_test.ngsp7bSrFunksVxPo")
            >>> print(response.message)
        """
        ...

    def _build_list_params(
            self,
            limit: int = 30,
            page: int = 1,
    ) -> dict[str, Any]:
        params: dict[str, Any] = {"limit": limit, "page": page}
        return {"params": params}

    def _check_create_params(self, data: NotchPayWebhookCreate) -> None:
        if not data.get("url"):
            raise NotchPayError("URL is required")
        if not data.get("events") or len(data.get("events", [])) == 0:
            raise NotchPayError("At least one event type is required")


class NotchPayWebhooks(_NotchPayWebhooks, NotchPayBaseResource):
    """Synchronous client for Notchpay webhooks management.

    This class allows performing all webhook-related operations
    synchronously.

    Example:
        >>> from notchpay import NotchPay
        >>> notchpay = NotchPay(api_key="your_api_key")
        >>> webhooks = notchpay.webhooks.list(limit=5)
        >>> print(f"Number of webhooks: {len(webhooks.items)}")
    """

    def list(
            self,
            limit: int = 30,
            page: int = 1,
    ) -> NotchPayWebhooksListResponse:
        params = super()._build_list_params(limit, page)
        return self._request(
            "GET",
            "/webhooks",
            response_model=NotchPayWebhooksListResponse,
            **params
        )

    def create(self, data: NotchPayWebhookCreate) -> NotchPayWebhookResponse:
        self._check_create_params(data)
        return self._request(
            "POST",
            "/webhooks",
            response_model=NotchPayWebhookResponse,
            json=data
        )

    def retrieve(self, webhook_id: str) -> NotchPayWebhookResponse:
        return self._request(
            "GET",
            f"/webhooks/{webhook_id}",
            response_model=NotchPayWebhookResponse,
        )

    def update(self, webhook_id: str, data: NotchPayWebhookUpdate) -> NotchPayWebhookResponse:
        return self._request(
            "PUT",
            f"/webhooks/{webhook_id}",
            response_model=NotchPayWebhookResponse,
            json=data
        )

    def delete(self, webhook_id: str) -> NotchPayResponse:
        return self._request(
            "DELETE",
            f"/webhooks/{webhook_id}",
            response_model=NotchPayResponse,
        )


class AsyncNotchPayWebhooks(_NotchPayWebhooks, AsyncNotchPayBaseResource):
    """Asynchronous client for Notchpay webhooks management.

    This class allows performing all webhook-related operations
    asynchronously.

    Example:
        >>> import asyncio
        >>> from notchpay import AsyncNotchPay
        >>> async def main():
        ...    notchpay = AsyncNotchPay("your_api_key")
        ...    webhooks = await notchpay.webhooks.list(limit=5)
        ...    print(f"Number of webhooks: {len(webhooks.items)}")
        >>> asyncio.run(main())
    """

    @override
    async def list(
            self,
            limit: int = 30,
            page: int = 1,
    ) -> NotchPayWebhooksListResponse:
        params = super()._build_list_params(limit, page)
        return await self._request(
            "GET",
            "/webhooks",
            response_model=NotchPayWebhooksListResponse,
            **params
        )

    async def create(self, data: NotchPayWebhookCreate) -> NotchPayWebhookResponse:
        self._check_create_params(data)
        return await self._request(
            "POST",
            "/webhooks",
            response_model=NotchPayWebhookResponse,
            json=data
        )

    async def retrieve(self, webhook_id: str) -> NotchPayWebhookResponse:
        return await self._request(
            "GET",
            f"/webhooks/{webhook_id}",
            response_model=NotchPayWebhookResponse,
        )

    async def update(self, webhook_id: str, data: NotchPayWebhookUpdate) -> NotchPayWebhookResponse:
        return await self._request(
            "PUT",
            f"/webhooks/{webhook_id}",
            response_model=NotchPayWebhookResponse,
            json=data
        )

    async def delete(self, webhook_id: str) -> NotchPayResponse:
        return await self._request(
            "DELETE",
            f"/webhooks/{webhook_id}",
            response_model=NotchPayResponse,
        )
