from glob import glob
from json import load, dump
from os import path, mkdir
from shutil import rmtree, copytree
from time import time
from uuid import uuid1

from pycs import ApplicationStatus
from pycs.observable import ObservableDict
from pycs.projects.Project import Project


class ProjectManager(ObservableDict):
    def __init__(self, app_status: ApplicationStatus):
        # TODO create projects folder if it does not exist
        self.app_status = app_status

        # initialize observable dict with no keys and
        # app_status object as parent
        super().__init__({}, app_status)
        app_status['projects'] = self

        # find projects
        for folder in glob('projects/*'):
            # load project.json
            with open(path.join(folder, 'project.json'), 'r') as file:
                project = Project(load(file), self)
                self[project['id']] = project

    def write_project(self, uuid):
        copy = self[uuid].copy()
        del copy['jobs']
        del copy['model']

        with open(path.join('projects', uuid, 'project.json'), 'w') as file:
            dump(copy, file, indent=4)

    def create_project(self, name, description, model):
        # create dict representation
        uuid = str(uuid1())

        # create project directory
        folder = path.join('projects', uuid)
        mkdir(folder)

        # copy model to project directory
        copytree(self.parent['models'][model]['path'], path.join(folder, 'model'))

        # create project object
        self[uuid] = Project({
            'id': uuid,
            'name': name,
            'description': description,
            'created': int(time()),
            'data': {},
            'labels': {},
            'jobs': {}
        }, self)

        # create project.json
        self.write_project(uuid)

    def update_project(self, uuid, update):
        # abort if uuid is no valid key
        if uuid not in self.keys():
            return

        # set values and write to disk
        self[uuid].update_properties(update)
        self.write_project(uuid)

    def delete_project(self, uuid):
        # abort if uuid is no valid key
        if uuid not in self.keys():
            return

        # delete project folder
        folder = path.join('projects', uuid)
        rmtree(folder)

        # delete project data
        del self[uuid]

    def predict(self, uuid, identifiers=None):
        # abort if uuid is no valid key
        if uuid not in self.keys():
            return

        project = self[uuid]

        # get identifiers
        if identifiers is None:
            identifiers = list(project['data'].keys())

        # run prediction
        project.predict(identifiers)

    def fit(self, uuid):
        # abort if uuid is no valid key
        if uuid not in self.keys():
            return

        project = self[uuid]

        # run fit
        project.fit()