import os
from urllib.parse import unquote
from notebook.base.handlers import IPythonHandler
from .connection import openbis_connections

class DataSetDownloadHandler(IPythonHandler):
    """Handle the requests for /openbis/dataset/connection/permId"""


    def download_data(self, conn, permId, downloadPath=None):
        if not conn.is_session_active():
            try:
                conn.login()
            except Exception as exc:
                self.set_status(500)
                self.write({
                    "reason" : 'connection to {} could not be established: {}'.format(conn.name, exc)
                })
                return

        try:
            dataset = conn.openbis.get_dataset(permId)
        except Exception as exc:
            self.set_status(404)
            self.write({
                "reason" : 'No such dataSet found: {}'.format(permId)
            })
            return

        # dataset was found, download the data to the disk
        try:
            destination = dataset.download(destination=downloadPath)
        except Exception as exc:
            self.set_status(500)
            self.write({
                "reason": 'Data for DataSet {} could not be downloaded: {}'.format(permId, exc)
            })
            return

        # return success message
        path = os.path.join(downloadPath, dataset.permId)
        self.write({
            'url'       : conn.url,
            'permId'    : dataset.permId,
            'path'      : path,
            'dataStore' : dataset.dataStore,
            'location'  : dataset.physicalData.location,
            'size'      : dataset.physicalData.size,
            'statusText': 'Data for DataSet {} was successfully downloaded to: {}.'.format(dataset.permId, path)
        })

    def get(self, **params):
        """Handle a request to /openbis/dataset/connection_name/permId
        download the data and return a message
        """

        try:
            conn = openbis_connections[params['connection_name']]
        except KeyError:
            self.set_status(404)
            self.write({
                "reason":'connection {} was not found'.format(params['connection_name'])
            })
            return

        results = self.download_data(conn=conn, permId=params['permId'], downloadPath=params['downloadPath'])


class DataSetTypesHandler(IPythonHandler):
    def get(self, **params):
        """Handle a request to /openbis/datasetTypes/connection_name
        """

        try:
            conn = openbis_connections[params['connection_name']]
        except KeyError:
            self.set_status(404)
            self.write({
                "reason":'connection {} was not found'.format(params['connection_name'])
            })
            return

        try:
            dataset_types = conn.openbis.get_dataset_types()
            dts = dataset_types.df.to_dict(orient='records')

            # get property assignments for every dataset-type
            # and add it to the dataset collection
            for dt in dts:
                dataset_type = conn.openbis.get_dataset_type(dt['code'])
                pa = dataset_type.get_propertyAssignments(including_vocabulary=True)
                pa_dicts = pa.to_dict(orient='records')
                for pa_dict in pa_dicts:
                    if pa_dict['dataType'] == 'CONTROLLEDVOCABULARY':
                        terms = conn.openbis.get_terms(pa_dict['vocabulary']['code'])
                        pa_dict['terms'] = terms.df[['code','label','description','official','ordinal']].to_dict(orient='records')

                dt['propertyAssignments'] = pa_dicts

            self.write({
                "dataSetTypes": dts
            })
            return

        except Exception as e:
            print(e)
            self.set_status(500)
            self.write({
                "reason":'Could not fetch dataset-types: {}'.format(e)
            })
            return


class DataSetUploadHandler(IPythonHandler):
    """Handle the POST requests for /openbis/dataset/connection_name"""

    def upload_data(self, conn, data):
        if not conn.is_session_active():
            try:
                conn.login()
            except Exception as e:
                print(e)
                self.set_status(500)
                self.write({
                    "reason": 'connection to {} could not be established: {}'.format(conn.name, e)
                })
                return

        errors = []
        print("--------------_HERE_-----------1")

        sample = None
        experiment = None

        if (data.get('entityIdentifier')):
            try:
                sample = conn.openbis.get_sample(data.get('entityIdentifier'))
            except Exception as e:
                pass
            if sample is None:
                try:
                    experiment = conn.openbis.get_experiment(data.get('entityIdentifier'))
                except Exception as e:
                    pass

            if sample is None and experiment is None:
                errors.append(
                    {"entityIdentifier" : 'No such sample or experiment: {}'.format(data.get('entityIdentifier')) }
                )
        else:
            errors.append(
                {"entityIdentifier": "please provide a sample or experiment identifier"}
            )

        print("--------------_HERE_-----------2")

        parents = []
        if data.get('parents'):
            parents = data.get('parents')
            for parent in parents:
                try:
                    conn.openbis.get_dataset(parent)
                except Exception as e:
                    errors.append({
                        "parent": "Parent DataSet not found: {}".format(parent)
                    })

        print("--------------_HERE_-----------2a")

        filenames = []
        for filename in data.get('files'):
            filename = unquote(filename)
            if os.path.isfile(filename):
                filenames.append(filename)
            else:
                errors.append({
                    "file": "File not found: {}".format(filename)
                })
        print("--------------_HERE_-----------3")

        try:
            dataset = conn.openbis.new_dataset(
                type   = data.get('type'),
                sample = sample,
                parents = parents,
                experiment = experiment,
                files  = filenames,
            )
        except Exception as e:
            print(e)
            errors.append({
                "create": 'Error while creating the dataset: {}'.format(e)
            })
        print("--------------_HERE_-----------4")

        # try to set the properties
        if data.get('props'):
            props = data.get('props')
            for prop, value in props.items():
                try:
                    setattr(dataset.props, prop.lower(), value)
                except Exception as e:
                    errors.append({
                        "prop."+prop : str(e)
                    })

        # check if any mandatory property is missing
        for prop_name, prop in dataset.props._property_names.items():
            if prop['mandatory']:
                if getattr(dataset.props, prop_name) is None or getattr(dataset.props, prop_name) == "":
                    errors.append({
                        "prop."+prop_name : "is mandatory"
                    })
        print("--------------_HERE_-----------5")

        # write errors back if already occured
        if errors:
            self.set_status(500)
            self.write({ "errors": errors })
            return
        print("--------------_HERE_-----------6")

        try:
            dataset.save()
        except Exception as e:
            errors.append({
                "save": 'Error while saving the dataset: {}'.format(e)
            })
        print("--------------_HERE_-----------7")

        # write errors back if they occured
        if errors:
            self.set_status(500)
            self.write({ "errors": errors })
        else:
            # ...or return a success message
            self.write({
                'status': 200,
                'statusText': 'Jupyter Notebook was successfully uploaded to: {} with permId: {}'.format(conn.name, dataset.permId)
            })

        print('Jupyter Notebook was successfully uploaded to: {} with permId: {}'.format(conn.name, dataset.permId))
        print("--------------_HERE_-----------8")

    def post(self, **params):
        """Handle a request to /openbis/dataset/connection_name/permId
        download the data and return a message
        """

        try:
            conn = openbis_connections[params['connection_name']]
        except KeyError:
            self.write({
                "reason": 'connection {} was not found'.format(params['connection_name'])
            })
            return

        data = self.get_json_body()
        print("Received DATA")
        print(data)
        self.upload_data(conn=conn,data=data)


class FileListHandler(IPythonHandler):

    def get(self, **params):
        """
        Returns the file list of the current working directory

        :param params:
        :return: dictionary of files, key is the fully qualified name,
                 value is the relative name (for display)
        """

        cwd = os.getcwd()
        files = {}
        for (dirpath, dirnames, filenames) in os.walk(cwd):
            if filenames:
                for filename in filenames:
                    # ignore hidden files
                    if filename.startswith('.'):
                        continue
                    # ignore hidden folders
                    if os.path.relpath(dirpath) != '.' \
                            and os.path.relpath(dirpath).startswith('.'):
                        continue
                    fqn = os.path.join(dirpath, filename)
                    files[fqn] = os.path.relpath(fqn, cwd)

        self.set_status(200)
        self.write({
            "files": files
        })