import os
import uuid

from eventlet import tpool
from flask import make_response, request, abort
from flask.views import View
from werkzeug import formparser

from pycs.database.Project import Project
from pycs.frontend.notifications.NotificationManager import NotificationManager
from pycs.util.FileOperations import file_info


class UploadFile(View):
    """
    save file uploads
    """
    # pylint: disable=arguments-differ
    methods = ['POST']

    def __init__(self, nm: NotificationManager):
        # pylint: disable=invalid-name
        self.nm = nm

        self.data_folder = None
        self.uuid = None
        self.name = None
        self.extension = None
        self.size = None

    def dispatch_request(self, project_id: int):
        # find project
        project = Project.get_or_404(project_id)

        # abort if external storage is used
        if project.external_data:
            return abort(400, "Project uses external data, but a file was uploaded")

        # get upload path and id
        self.data_folder = project.data_folder
        self.uuid = str(uuid.uuid1())

        # parse upload data
        _, _, files = tpool.execute(formparser.parse_form_data,
                                    request.environ,
                                    stream_factory=self.custom_stream_factory)

        # abort if there is no file entry in uploaded data
        if 'file' not in files.keys():
            return abort(400, "No file entry was found in uploaded data")

        # detect file type
        try:
            ftype, frames, fps = tpool.execute(file_info,
                                               self.data_folder,
                                               self.uuid,
                                               self.extension)
        except ValueError as exception:
            return abort(400, str(exception))

        file, _ = project.add_file(
            uuid=self.uuid,
            file_type=ftype,
            name=self.name,
            extension=self.extension,
            size=self.size,
            filename=self.uuid,
            frames=frames,
            fps=fps)

        # send update
        self.nm.create_file(file)

        # return default success response
        return make_response()

    def custom_stream_factory(self, total_content_length, filename, content_type,
                              content_length=None):
        """
        save some useful information and open a file handler to save the uploaded file to

        :param total_content_length:
        :param filename:
        :param content_type:
        :param content_length:
        :return:
        """
        # pylint: disable=unused-argument
        # set relevant properties
        self.name, self.extension = os.path.splitext(filename)

        if content_length is not None and content_length > 0:
            self.size = content_length
        else:
            self.size = total_content_length

        # open file handler
        file_path = os.path.join(self.data_folder, f'{self.uuid}{self.extension}')
        #pylint: disable=consider-using-with
        return open(file_path, 'wb')
