import collections.abc
import contextlib

from rest_framework import (
    decorators,
    exceptions,
    mixins,
    permissions,
    response,
    status,
    viewsets,
)

import django_filters

from ... import models
from .. import mixins as core_mixins


class BaseExportJobViewSet(
    mixins.ListModelMixin,
    mixins.RetrieveModelMixin,
    viewsets.GenericViewSet,
):
    """Base viewset for managing export jobs."""

    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = core_mixins.ExportStartActionMixin.export_detail_serializer_class  # noqa: E501
    queryset = models.ExportJob.objects.all()
    filterset_class: django_filters.rest_framework.FilterSet | None = None
    search_fields: collections.abc.Sequence[str] = ("id",)
    ordering: collections.abc.Sequence[str] = (
        "id",
    )
    ordering_fields: collections.abc.Sequence[str] = (
        "id",
        "created",
        "modified",
    )

    def __init_subclass__(cls) -> None:
        """Dynamically create an cancel api endpoints.

        Need to do this to enable action and correct open-api spec generated by
        drf_spectacular.

        """
        super().__init_subclass__()
        decorators.action(
            methods=["POST"],
            detail=True,
        )(cls.cancel)
        # Correct specs of drf-spectacular if it is installed
        with contextlib.suppress(ImportError):
            from drf_spectacular.utils import extend_schema, extend_schema_view
            if hasattr(cls, "get_export_detail_serializer_class"):
                response_serializer = cls().get_export_detail_serializer_class()  # noqa: E501
            else:
                response_serializer = cls().get_serializer_class()
            extend_schema_view(
                cancel=extend_schema(
                    request=None,
                    responses={
                        status.HTTP_200_OK: response_serializer,
                    },
                ),
            )(cls)

    def cancel(self, *args, **kwargs) -> response.Response:
        """Cancel export job that is in progress."""
        job: models.ExportJob = self.get_object()

        try:
            job.cancel_export()
        except ValueError as error:
            raise exceptions.ValidationError(error.args[0]) from error

        serializer = self.get_serializer(instance=job)
        return response.Response(
            status=status.HTTP_200_OK,
            data=serializer.data,
        )

class ExportJobViewSet(
    core_mixins.ExportStartActionMixin,
    BaseExportJobViewSet,
):
    """Base API viewset for ExportJob model.

    Based on resource_class it will generate an endpoint which will allow to
    start an export of model which was specified in resource_class. This
    endpoint will support filtration based on FilterSet class specified in
    resource. On success this endpoint we return an instance of export, to
    get status of job, just use detail(retrieve) endpoint.

    """

    export_action_name = "start"
    export_action_url = "start"

    def get_queryset(self):
        """Filter export jobs by resource used in viewset."""
        if self.action == getattr(self, "export_action", ""):
            # To make it consistent and for better support of drf-spectacular
            return super().get_queryset()  # pragma: no cover
        return super().get_queryset().filter(
            resource_path=self.resource_class.class_path,
        )


class ExportJobForUserViewSet(
    core_mixins.LimitQuerySetToCurrentUserMixin,
    ExportJobViewSet,
):
    """Viewset for providing export feature to users."""

class BaseExportJobForUserViewSet(
    core_mixins.LimitQuerySetToCurrentUserMixin,
    BaseExportJobViewSet,
):
    """Viewset for providing export job management to users."""
