from flask import make_response, request, abort
from flask.views import View

from pycs.database.Database import Database
from pycs.interfaces.MediaStorage import MediaStorage
from pycs.jobs.JobGroupBusyException import JobGroupBusyException
from pycs.jobs.JobRunner import JobRunner
from pycs.util.PipelineCache import PipelineCache


class FitModel(View):
    """
    use annotated data to fit a model
    """
    # pylint: disable=arguments-differ
    methods = ['POST']

    def __init__(self, db: Database, jobs: JobRunner, pipelines: PipelineCache):
        # pylint: disable=invalid-name
        self.db = db
        self.jobs = jobs
        self.pipelines = pipelines

    def dispatch_request(self, project_id):
        # extract request data
        data = request.get_json(force=True)

        if 'fit' not in data or data['fit'] is not True:
            return abort(400)

        # find project
        project = self.db.project(project_id)
        if project is None:
            return abort(404)

        # create job
        try:
            self.jobs.run(project,
                          'Model Interaction',
                          f'{project.name} (fit model with new data)',
                          f'{project.name}/model-interaction',
                          self.load_and_fit, self.db, project.identifier)
        except JobGroupBusyException:
            return abort(400)

        return make_response()

    @staticmethod
    def load_and_fit(database: Database, pipelines: PipelineCache, project_id: int):
        """
        load the pipeline and call the fit function

        :param database: database object
        :param pipelines: pipeline cache
        :param project_id: project id
        """
        database_copy = None
        pipeline = None

        # create new database instance
        try:
            database_copy = database.copy()
            project = database_copy.project(project_id)
            model = project.model()
            storage = MediaStorage(database_copy, project_id)

            # load pipeline
            try:
                pipeline = pipelines.load_from_root_folder(project, model.root_folder)
                yield from pipeline.fit(storage)
            except TypeError:
                pass
            finally:
                if pipeline is not None:
                    pipelines.free_instance(model.root_folder)
        finally:
            if database_copy is not None:
                database_copy.close()
