import inspect
import time

from core.decorators import instance
from core.logger import Logger


@instance()
class JobScheduler:
    def __init__(self):
        self.logger = Logger(__name__)
        self.jobs = []
        self.job_id_index = 0

    def check_for_scheduled_jobs(self, timestamp):
        while self.jobs and self.jobs[0]["time"] <= timestamp:
            try:
                job = self.jobs.pop(0)
                job["callback"](job["time"], *job["args"], **job["kwargs"])
            except Exception as e:
                self.logger.warning("Error processing scheduled job", e)

    def delayed_job(self, callback, delay, *args, **kwargs):
        """
        Args:
            callback: (time: Int, *args, *kwargs) -> void)
            delay: int
            *args
            **kwargs
        """

        return self.scheduled_job(callback, int(time.time()) + delay, *args, **kwargs)

    def scheduled_job(self, callback, scheduled_time, *args, **kwargs):
        """
        Args:
            callback: (time: Int, *args, *kwargs) -> void)
            scheduled_time: int
            *args
            **kwargs
        """

        if len(inspect.signature(callback).parameters) < 1:
            raise Exception(f"Incorrect number of arguments for handler '{callback.__module__}.{callback.__name__}()'")

        job_id = self._get_next_job_id()
        new_job = {
            "id": job_id,
            "callback": callback,
            "args": args,
            "kwargs": kwargs,
            "time": scheduled_time
        }

        self._insert_job(new_job)
        return job_id

    def cancel_job(self, job_id):
        for index, job in enumerate(self.jobs):
            if job["id"] == job_id:
                return self.jobs.pop(index)
        return None

    def _insert_job(self, new_job):
        for index, job in enumerate(self.jobs):
            if job["time"] > new_job["time"]:
                self.jobs.insert(index, new_job)
                return
        self.jobs.append(new_job)

    def _get_next_job_id(self):
        self.job_id_index += 1
        return self.job_id_index
