import os.path
import shutil
import zipfile
from typing import Callable

from django.core.files.storage import default_storage
from django.utils._os import safe_join
from django.utils.translation import gettext as _
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ValidationError

from common.api import JMSBulkModelViewSet
from common.serializers import FileSerializer
from terminal import serializers
from terminal.models import VirtualAppPublication, VirtualApp

__all__ = ['VirtualAppViewSet', 'VirtualAppPublicationViewSet']


class UploadMixin:
    get_serializer: Callable
    request: Request
    get_object: Callable

    def extract_zip_pkg(self):
        serializer = self.get_serializer(data=self.request.data)
        serializer.is_valid(raise_exception=True)
        file = serializer.validated_data['file']
        save_to = 'virtual_apps/{}'.format(file.name + '.tmp.zip')
        if default_storage.exists(save_to):
            default_storage.delete(save_to)
        rel_path = default_storage.save(save_to, file)
        path = default_storage.path(rel_path)
        extract_to = default_storage.path('virtual_apps/{}.tmp'.format(file.name))
        if os.path.exists(extract_to):
            shutil.rmtree(extract_to)
        try:
            with zipfile.ZipFile(path) as zp:
                if zp.testzip() is not None:
                    raise ValidationError({'error': _('Invalid zip file')})
                zp.extractall(extract_to)
        except RuntimeError as e:
            raise ValidationError({'error': _('Invalid zip file') + ': {}'.format(e)})
        tmp_dir = safe_join(extract_to, file.name.replace('.zip', ''))
        return tmp_dir

    @action(detail=False, methods=['post'], serializer_class=FileSerializer)
    def upload(self, request, *args, **kwargs):
        tmp_dir = self.extract_zip_pkg()
        manifest = VirtualApp.validate_pkg(tmp_dir)
        name = manifest['name']
        instance = VirtualApp.objects.filter(name=name).first()
        if instance:
            return Response({'error': 'virtual app already exists: {}'.format(name)}, status=400)

        app, serializer = VirtualApp.install_from_dir(tmp_dir)
        return Response(serializer.data, status=201)


class VirtualAppViewSet(UploadMixin, JMSBulkModelViewSet):
    queryset = VirtualApp.objects.all()
    serializer_class = serializers.VirtualAppSerializer
    filterset_fields = ['name', 'is_active']
    search_fields = ['name', 'image_name']
    rbac_perms = {
        'upload': 'terminal.add_virtualapp',
    }


class VirtualAppPublicationViewSet(viewsets.ModelViewSet):
    queryset = VirtualAppPublication.objects.all()
    serializer_class = serializers.VirtualAppPublicationSerializer
    filterset_fields = ['app__name', 'provider__name', 'status']
    search_fields = ['app__name', 'provider__name', ]