import uuid
import os
from pathlib import Path

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

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

from ichec_django_core.models import Member, FormFieldValue
from ichec_django_core.views import (
    ObjectFileDownloadView,
    SearchableModelViewSet,
    CustomDjangoModelPermissions,
)

from marinerg_test_access.models import AccessApplication
from marinerg_test_access.serializers import (
    AccessApplicationCreateSerializer,
    AccessApplicationDetailSerializer,
)


def is_on_access_board(application, user):
    if user.id == application.call.board_chair.id:
        return True

    for member in application.call.board_members.all():
        if member.id == user.id:
            return True
    return False


def is_call_coordinator(application, user):
    return application.call.coordinator.id == user.id


def is_on_board(application, user):
    return is_call_coordinator(application, user) or is_on_access_board(
        application, user
    )


class ApplicationPermissions(CustomDjangoModelPermissions):

    intercept_methods: list = ["GET", "PUT", "POST"]
    safe_methods: list = ["HEAD", "OPTIONS"]

    def object_perm_check(self, obj, request) -> bool:

        # All authenticated users can post
        if request.method == "POST":
            return True

        # Applicant can get, put, post
        if obj.applicant.id == request.user.id:
            return True

        # Review board can get
        if is_on_board(obj, request.user) and request.method == "GET":
            return True

        # Use Django permissions for everything else
        return False


class AccessApplicationViewSet(SearchableModelViewSet):
    model = AccessApplication
    queryset = AccessApplication.objects.all()
    serializer_class = AccessApplicationDetailSerializer
    permission_classes = [
        permissions.IsAuthenticated,
        ApplicationPermissions,
    ]

    ordering: tuple[str, ...] = ("created_at",)
    search_fields = ["applicant__usename"]

    serializers = {
        "retrieve": AccessApplicationDetailSerializer,
        "list": AccessApplicationDetailSerializer,
        "create": AccessApplicationCreateSerializer,
        "update": AccessApplicationDetailSerializer,
        "partial_update": AccessApplicationDetailSerializer,
    }

    filterset_fields = (
        "applicant__id",
        "call__id",
        "facilities__id",
        "chosen_facility__id",
        "status",
    )

    @action(detail=True, url_path="summary", url_name="summary")
    def summary(self, request, pk=None):
        return self.download(request, "summary")

    def perform_create(self, serializer):
        serializer.save(applicant=Member.objects.get(id=self.request.user.id))

    def get_queryset(self):
        """
        If user has Application view perm return all applications
        Otherwise only return if they are the applicant or
        member of the review board
        """

        if self.has_model_view_permission():
            return ModelViewSet.get_queryset(self)

        queryset = ModelViewSet.get_queryset(self)
        queryset = queryset.filter(
            Q(applicant__id=self.request.user.id)
            | Q(call__board_chair__id=self.request.user.id)
            | Q(call__board_members__id=self.request.user.id)
            | Q(call__coordinator__id=self.request.user.id)
        ).distinct()
        return queryset


class ApplicationFieldDownloadView(ObjectFileDownloadView):

    model = AccessApplication
    permission_classes = [
        permissions.IsAuthenticated,
        ApplicationPermissions,
    ]

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

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

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

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


class ApplicationFieldUploadView(GenericAPIView):

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

    def put(self, request, *args, **kwargs):
        value = FormFieldValue.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_values")
        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.asset.original_file_name = filename
        value.asset.name = str(storage_dir / written_path)
        value.save()

        return Response(status=204)
