from app import app_plugins, plugin_path, db, app_dir
from app.models import IntervalSchedulerJob as DatabaseIntervalSchedulerJob, CronSchedulerJob as DatabaseCronSchedulerJob
from app.lib.scheduler import CronJob, IntervalJob, Job
from inspect import isclass
from importlib import import_module
from flask import Flask
from os import path

__all__ = ('add_interval_jobs_to_database', 'add_cron_jobs_to_database')

def get_blueprint_jobs_modules(job_type):
    """Crawls the blueprint directory for the 'models.py' file and returns the import path as a string for any
    of the files that are found

    :return:
    """
    if not issubclass(job_type, Job):
        raise TypeError('job_type invalid, must be CronJob or IntervalJob')

    for plugin in app_plugins:
        expected_file = path.join(plugin_path, plugin, job_type.filename)
        if path.exists(expected_file):
            yield f'{app_dir}.plugins.{plugin}.{job_type.filename.replace(".py", "")}'


def job_instantiator(modules_path, job_type):
    """Uses get_blueprint_models to get the import path strings for all blueprint database models and instantiates
    them as objects and returns them to be imported into the flask app

    Note this does filter out any classes that start with '__' and it will filter out the base class

    :return:
    """
    for mod in modules_path:
        module = import_module(mod)

        # Gets all classes in the module that are not the base model class
        objects = [getattr(module, x) for x in dir(module) if not x.startswith('__') and getattr(module, x) is not job_type]
        for x in objects:
            if isclass(x) and issubclass(x, Job):
                yield x


def get_jobs(job_type):
    jobs = []
    mods = get_blueprint_jobs_modules(job_type)
    for x in job_instantiator(mods, job_type):
        jobs.append(x)
    return jobs


def add_cron_jobs_to_database(app):
    if not isinstance(app, Flask):
        raise AttributeError('app argument MUST be a subclass of Flask object')

    jobs = get_jobs(CronJob)
    for job in jobs:
        job_obj = job(app)
        with app.app_context():
            db_entry = DatabaseCronSchedulerJob().query.filter_by(name=job_obj.__job_name__).first()
            if not db_entry:
                db.session.add(DatabaseCronSchedulerJob(name=job_obj.__job_name__))
                db.session.commit()


def add_interval_jobs_to_database(app):
    if not isinstance(app, Flask):
        raise AttributeError('app argument MUST be a subclass of Flask object')

    jobs = get_jobs(IntervalJob)
    for job in jobs:
        job_obj = job(app)
        with app.app_context():
            db_entry = DatabaseIntervalSchedulerJob().query.filter_by(name=job_obj.__job_name__).first()
            if not db_entry:
                db.session.add(DatabaseIntervalSchedulerJob(name=job_obj.__job_name__))
                db.session.commit()



from app import app_plugins, plugin_path, db, app_dir
from app.models import IntervalSchedulerJob as DatabaseIntervalSchedulerJob, CronSchedulerJob as DatabaseCronSchedulerJob
from app.lib.scheduler import CronJob, IntervalJob, Job
from inspect import isclass
from importlib import import_module
from flask import Flask
from os import path

__all__ = ('add_interval_jobs_to_database', 'add_cron_jobs_to_database')

def get_blueprint_jobs_modules(job_type):
    """Crawls the blueprint directory for the 'models.py' file and returns the import path as a string for any
    of the files that are found

    :return:
    """
    if not issubclass(job_type, Job):
        raise TypeError('job_type invalid, must be CronJob or IntervalJob')

    for plugin in app_plugins:
        expected_file = path.join(plugin_path, plugin, job_type.filename)
        if path.exists(expected_file):
            yield f'{app_dir}.plugins.{plugin}.{job_type.filename.replace(".py", "")}'


def job_instantiator(modules_path, job_type):
    """Uses get_blueprint_models to get the import path strings for all blueprint database models and instantiates
    them as objects and returns them to be imported into the flask app

    Note this does filter out any classes that start with '__' and it will filter out the base class

    :return:
    """
    for mod in modules_path:
        module = import_module(mod)

        # Gets all classes in the module that are not the base model class
        objects = [getattr(module, x) for x in dir(module) if not x.startswith('__') and getattr(module, x) is not job_type]
        for x in objects:
            if isclass(x) and issubclass(x, Job):
                yield x


def get_jobs(job_type):
    jobs = []
    mods = get_blueprint_jobs_modules(job_type)
    for x in job_instantiator(mods, job_type):
        jobs.append(x)
    return jobs


def add_cron_jobs_to_database(app):
    if not isinstance(app, Flask):
        raise AttributeError('app argument MUST be a subclass of Flask object')

    jobs = get_jobs(CronJob)
    for job in jobs:
        job_obj = job(app)
        with app.app_context():
            db_entry = DatabaseCronSchedulerJob().query.filter_by(name=job_obj.__job_name__).first()
            if not db_entry:
                db.session.add(DatabaseCronSchedulerJob(name=job_obj.__job_name__))
                db.session.commit()


def add_interval_jobs_to_database(app):
    if not isinstance(app, Flask):
        raise AttributeError('app argument MUST be a subclass of Flask object')

    jobs = get_jobs(IntervalJob)
    for job in jobs:
        job_obj = job(app)
        with app.app_context():
            db_entry = DatabaseIntervalSchedulerJob().query.filter_by(name=job_obj.__job_name__).first()
            if not db_entry:
                db.session.add(DatabaseIntervalSchedulerJob(name=job_obj.__job_name__))
                db.session.commit()



Crawls the blueprint directory for the 'models.py' file and returns the import path as a string for any     of the files that are found      :return: Uses get_blueprint_models to get the import path strings for all blueprint database models and instantiates     them as objects and returns them to be imported into the flask app      Note this does filter out any classes that start with '__' and it will filter out the base class      :return: # Gets all classes in the module that are not the base model class