import calendar

from django.contrib.auth.models import Group
from django.http import JsonResponse
from django.conf import settings
from django.http import Http404, HttpResponseNotModified
from django.views.static import was_modified_since

from django_filters import rest_framework as df_filters
from rest_framework import permissions, viewsets, filters, status
from rest_framework.response import Response
from django_downloadview import DownloadResponse

from ichec_django_core.serializers import GroupSerializer

from .permissions import get_permission
from .files import upload_file

BASE_ORDERING_FIELDS = ("id", "created_at", "updated_at")


def server_manifest(_request):

    return JsonResponse(
        {
            "login_url": (
                "oidc/authenticate" if settings.WITH_OIDC else "accounts/login"
            ),
            "logout_url": "oidc/logout" if settings.WITH_OIDC else "api-auth/logout",
            "supports_cors": settings.DEBUG,
        }
    )


class BaseModelViewSet(viewsets.ModelViewSet):

    def filter_self_or_permission(self, queryset):
        if self.request.method == "GET":
            if self.request.user:
                if not self.has_model_view_permission():
                    return self.filter_self(queryset)
            else:
                return None
        return queryset

    def filter_self(self, queryset):
        return queryset

    def has_model_view_permission(self) -> bool:
        return self.self_has_permission(get_permission(self.model, "view"))

    def self_has_permission(self, perm: str) -> bool:
        return self.request.user.has_perm(perm)

    def get_queryset(self):
        """
        On classes that support public viewing filter by public instances
        on view sets.
        """

        queryset = super().get_queryset()
        if not self.request.user.is_authenticated:
            return queryset.filter(public=True)
        return queryset

    def get_serializer_class(self):
        if hasattr(self, "serializers"):
            if (
                not self.request.user.is_authenticated
                and "public_" + self.action in self.serializers
            ):
                return self.serializers["public_" + self.action]
            return self.serializers.get(self.action, self.serializer_class)
        return super().get_serializer_class()

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        self.check_field_permissions(request, serializer)

        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(
            serializer.data, status=status.HTTP_201_CREATED, headers=headers
        )

    def update(self, request, *args, **kwargs):

        partial = kwargs.pop("partial", False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)

        self.check_field_permissions(request, serializer)

        self.perform_update(serializer)

        if getattr(instance, "_prefetched_objects_cache", None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_destroy(self, instance):
        """
        Override from DRF DestroyModelMixin. Intercept the delete request
        and if supported by the model type and settings do a soft delete.
        """
        if settings.SOFT_DELETE and hasattr(instance, "active"):
            setattr(instance, "active", False)
            instance.save()
        else:
            instance.delete()

    def check_field_permissions(self, _request, _serializer):
        pass

    def upload(self, request, field: str, extensions: tuple[str, ...]):
        """
        Allow upload to particular endpoints using DRF extra action decorators
        on ViewSets
        """

        return upload_file(
            request, self.get_object(), self.model._meta.model_name, field, extensions
        )

    def _was_modified_since(self, file_instance, since):
        try:
            return file_instance.was_modified_since(since)
        except (AttributeError, NotImplementedError):
            try:
                modification_time = calendar.timegm(
                    file_instance.modified_time.utctimetuple()
                )
            except (AttributeError, NotImplementedError) as e:
                print("!=======!", e)
                return True
            else:
                return was_modified_since(since, modification_time)

    def download(self, request, field):
        """
        Allow file download by DRF extra action decorators
        """

        instance = self.get_object()
        file_field = getattr(instance, field)
        if not file_field:
            raise Http404()

        since = request.headers.get("if-modified-since", None)
        if since is not None:
            if not self._was_modified_since(file_field, since):
                return HttpResponseNotModified()

        filename = file_field.name
        basename = str(field + "." + filename.split(".")[-1])
        return DownloadResponse(file_instance=file_field, basename=basename)


class SearchableModelViewSet(BaseModelViewSet):
    filter_backends = [
        df_filters.DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]
    ordering_fields: tuple[str, ...] = BASE_ORDERING_FIELDS
    ordering: tuple[str, ...] = ("id",)


class GroupViewSet(SearchableModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer
    permission_classes = [
        permissions.IsAuthenticated,
        permissions.DjangoModelPermissions,
    ]

    ordering_fields: tuple[str, ...] = ("name",)
    ordering: tuple[str, ...] = ("name",)
    search_fields: tuple[str, ...] = ("name",)

    def get_queryset(self):
        queryset = Group.objects.all()
        user_id = self.request.query_params.get("user")
        if user_id is not None:
            queryset = queryset.filter(user__id=user_id)
        return queryset
