import uuid
import os
from pathlib import Path

from django.conf import settings
from django.http import Http404
from django.core.files.storage import FileSystemStorage
from django.db.models import Q

from rest_framework.viewsets import ModelViewSet
from rest_framework import permissions, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.parsers import FileUploadParser

from ichec_django_core.models import FormField
from ichec_django_core.views import (
    SearchableModelViewSet,
    ObjectFileDownloadView,
    ObjectFileUploadView,
    PublicViewOwnerFullOrDjangoModelPermissions,
    OwnerFullOrDjangoModelPermissions,
)

from marinerg_test_access.models import AccessCall, AccessCallFacilityReview
from marinerg_test_access.serializers import (
    AccessCallListSerializer,
    AccessCallDetailSerializer,
    AccessCallPublicDetailSerializer,
    AccessCallReviewerDetailSerializer,
    AccessCallFacilityReviewSerializer,
)


class CoordinatorEditOrDjangoModelPermissions(OwnerFullOrDjangoModelPermissions):
    owner_field = "coordinator"


class PublicViewCoordinatorEditOrDjangoModelPermissions(
    PublicViewOwnerFullOrDjangoModelPermissions
):
    owner_field = "coordinator"


def is_reviewer(call, request):
    for member in call.board_members.all():
        if member.id == request.user.id:
            return True
    return False


def is_coordinator_or_chair(call, request):
    return (
        call.coordinator.id == request.user.id or call.board_chair.id == request.user.id
    )


class AccessCallViewSet(SearchableModelViewSet):
    model = AccessCall
    queryset = AccessCall.objects.filter(active=True)
    serializer_class = AccessCallListSerializer

    serializers = {
        "retrieve": AccessCallDetailSerializer,
        "public_retrieve": AccessCallPublicDetailSerializer,
        "reviewer_retrieve": AccessCallReviewerDetailSerializer,
        "list": AccessCallListSerializer,
        "create": AccessCallDetailSerializer,
        "update": AccessCallDetailSerializer,
        "partial_update": AccessCallDetailSerializer,
    }

    permission_classes = [
        PublicViewCoordinatorEditOrDjangoModelPermissions,
    ]

    ordering_fields = ("status",)
    ordering: tuple[str, ...] = ("-closing_date",)
    search_fields = ["title"]

    @action(
        detail=True,
        url_path="applications_summary",
        url_name="applications_summary",
        permission_classes=[CoordinatorEditOrDjangoModelPermissions],
    )
    def applications_summary(self, request, pk=None):
        return self.download(request, "applications_summary")

    @action(
        detail=True,
        url_path="review_template",
        url_name="review_template",
        permission_classes=[CoordinatorEditOrDjangoModelPermissions],
    )
    def review_template(self, request, pk=None):
        return self.download(request, "review_template")

    def get_queryset(self):
        """
        Public users can only see public calls
        Registered users can see public calls or ones they are on the board of
        Members can see all calls
        Users with view permission can see all calls
        """

        queryset = ModelViewSet.get_queryset(self)

        if not self.request.user.is_authenticated:
            queryset = queryset.filter(public=True)
        elif not self.has_model_view_permission():
            queryset = queryset.filter(
                Q(public=True)
                | Q(board_members__id=self.request.user.id)
                | Q(board_chair__id=self.request.user.id)
                | Q(coordinator__id=self.request.user.id)
            ).distinct()
        return queryset

    def get_serializer_class(self):

        if self.action == "retrieve":
            prefix = ""
            if not self.request.user.is_authenticated:
                prefix = "public_"
            elif self.has_model_view_permission():
                prefix = ""
            elif is_coordinator_or_chair(self.get_object(), self.request):
                prefix = ""
            elif is_reviewer(self.get_object(), self.request):
                prefix = "reviewer_"
            else:
                prefix = "public_"
            return self.serializers[prefix + self.action]
        return self.serializers.get(self.action, self.serializer_class)


class AccessCallReviewTemplateUploadView(ObjectFileUploadView):
    model = AccessCall
    queryset = AccessCall.objects.all()
    file_field = "review_template"
    extensions = ("docx", "doc", "odt", "pdf")
    permission_classes = [
        permissions.IsAuthenticated,
        CoordinatorEditOrDjangoModelPermissions,
    ]


class AccessCallFieldDownloadView(ObjectFileDownloadView):

    model = AccessCall
    permission_classes = [
        permissions.IsAuthenticated,
        CoordinatorEditOrDjangoModelPermissions,
    ]

    def get_file(self):
        if not self.value.template:
            raise Http404
        return self.value.template

    def get_basename(self):
        ext = self.value.template.name.split(".")[-1]
        return f"access_call_{self.value.key}.{ext}"

    def get(self, request, *args, **kwargs):
        self.authenticate(request)
        self.has_permission(request)

        self.object = self.get_object()
        self.value = FormField.objects.get(id=kwargs["value_id"])
        return super(ObjectFileDownloadView, self).get(request, *args, **kwargs)


class AccessCallFieldUploadView(GenericAPIView):

    model = AccessCall
    queryset = AccessCall.objects.all()
    parser_classes = [FileUploadParser]
    permission_classes = [
        permissions.IsAuthenticated,
        CoordinatorEditOrDjangoModelPermissions,
    ]
    extensions = (".odt", ".doc", ".docx", ".pdf")

    def put(self, request, *args, **kwargs):
        value = FormField.objects.get(id=kwargs["value_id"])

        media_root = Path(settings.MEDIA_ROOT)
        instance = self.get_object()
        storage_dir = Path(
            f"{self.model._meta.model_name}/{instance.id}/field_templates"
        )
        os.makedirs(media_root / storage_dir, exist_ok=True)

        filename = request.data["file"].name

        _, ext = os.path.splitext(filename)
        if ext not in self.extensions:
            return Response(
                {"reason": "File extension not supported"},
                status=400,
            )

        file_path = f"{uuid.uuid4()}{ext}"
        written_path = FileSystemStorage(location=media_root / storage_dir).save(
            file_path, request.data["file"]
        )

        value.template.original_file_name = filename
        value.template.name = str(storage_dir / written_path)
        value.save()

        return Response(status=204)


class AccessCallFacilityReviewViewSet(viewsets.ModelViewSet):
    queryset = AccessCallFacilityReview.objects.all().order_by("id")
    serializer_class = AccessCallFacilityReviewSerializer
    permission_classes = [
        permissions.IsAuthenticated,
        permissions.DjangoModelPermissions,
    ]
