import logging
import os

from django.conf import settings
from django.db.models import Q
from django.http import JsonResponse, HttpResponseForbidden, HttpResponseNotFound
from rest_framework.decorators import action
from rest_framework.generics import get_object_or_404, GenericAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, DestroyModelMixin
from rest_framework.viewsets import GenericViewSet

from core.mixins import ApiPermissionCheckMixin
from core.utils import store_file_response
from devices.services.endpoint.endpoint_antivirus import EndpointAntivirusService
from perms.models import Perm
from storage.exception import StorageException
from storage.models import DataStorage
from storage.serializers import AntivirusStorageSerializer, StorageSerializer, StorageListSerializer

_log = logging.getLogger()


class StorageViewSet(ApiPermissionCheckMixin,
                     ListModelMixin,
                     RetrieveModelMixin,
                     DestroyModelMixin,
                     GenericViewSet):
    console_permissions = [Perm.can_view_storage]
    ordering_fields = ['id', 'format', 'size', 'description', 'created', 'filename']

    def get_serializer_class(self):
        if self.action == 'list':
            return StorageListSerializer
        return StorageSerializer

    def get_queryset(self):
        queryset = DataStorage.objects
        user = self.request.user
        storage_filter = Q(user=user)

        if not user.is_superuser:
            if user.has_perm(Perm.perm_req(Perm.can_download_rotation_files)):
                storage_filter |= Q(type=DataStorage.Type.DB_DUMP)
            queryset = queryset.filter(storage_filter).distinct()
        return queryset.all()

    class Meta:
        model = DataStorage

    @action(detail=True, name='download', methods=['GET'])
    def download(self, request, *args, **kwargs):
        cur = get_object_or_404(DataStorage, pk=kwargs['pk'])
        if cur.type == DataStorage.Type.DB_DUMP and not request.user.has_perm(
                Perm.perm_req(Perm.can_download_rotation_files)):
            _log.error(
                f'User [{request.user}] try to get storage with id [{cur.id}] without permissions [can_download_rotation_files] ')
            return HttpResponseForbidden()

        # Check that user owns this file
        if not request.user.is_superuser and cur.type != DataStorage.Type.DB_DUMP:
            if request.user != cur.user:
                _log.error(f'User [{request.user}] try to get storage with id [{id}] that belongs to [{cur.user}]')
                return HttpResponseForbidden()

        # Check that file exists
        file_path = os.path.join(settings.MEDIA_ROOT, cur.file.name)
        if not os.path.exists(file_path):
            _log.error(f'User [{request.user}] try to download file [{cur.pk}:{cur.file.name}] but file doesnt exist')
            return HttpResponseNotFound()

        _log.info(f'User [{request.user}] start download file [{cur.pk}:{cur.file.name}]')
        cur.save()
        return store_file_response(file_path)


class AntivirusStorageView(ApiPermissionCheckMixin,
                           GenericAPIView):
    serializer_class = AntivirusStorageSerializer
    console_permissions = [Perm.can_view_storage]

    def post(self, request, *args, **kwargs):
        if 'file' not in request.FILES:
            raise StorageException({'status': 'error', 'detail': 'Upload file is required'})
        # Calculating file size for storage instance info
        file = request.FILES['file']
        file.seek(0, os.SEEK_END)
        file_length = file.tell()
        # Setting up necessary service field values for storage field
        request.data['user'] = request.user.pk
        request.data['size'] = file_length
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            DataStorage.objects.filter(format=DataStorage.Type.CLAMAV).delete()
            serializer.save()
            _log.info('Update ClamAV signature database in storage')
            EndpointAntivirusService.all_update(True)
            return JsonResponse({'status': 'ok'})
        else:
            raise StorageException({'status': 'err', 'message': serializer.errors})
