import threading
import time

import schedule

from flyers import logger, datetimes, concurrents


class TimeUnit(object):
    SECONDS = 'seconds'
    MINUTES = 'minutes'
    HOURS = 'hours'
    DAYS = 'days'


# Beta
class BaseSchedulerTask(object):
    def __init__(self, name: str,
                 interval: int,
                 time_unit: str = TimeUnit.SECONDS,
                 run_at_start: bool = False, max_retries: int = 3,
                 loop_daly_seconds: int = 1):
        self.name = name
        self.interval = interval
        self.time_unit = time_unit
        self.run_at_start = run_at_start
        self.max_retries = max_retries
        self.loop_daly = loop_daly_seconds

        self.retries = 0
        self.start_time = datetimes.get_timestamp_ms()
        self.end_time = None
        self.end = False
        self.lock = threading.Lock()

    def start(self, *args, **kwargs):
        job = schedule.Job(self.interval, schedule.default_scheduler)
        job.unit = self.time_unit

        if self.run_at_start:
            self._execute(*args, **kwargs)
        job.do(self.__try_execute, *args, **kwargs)

        while not self.end:
            schedule.run_pending()
            time.sleep(self.loop_daly)

    def stop(self):
        with self.lock:
            self.end = True
            self.end_time = datetimes.get_timestamp_ms()

    def is_end(self):
        return self.end

    def get_cost_time(self) -> str:
        if self.end:
            et = self.end_time
        else:
            et = datetimes.get_timestamp_ms()
        return datetimes.format_duration(self.start_time, et)

    def __try_execute(self, *args, **kwargs):
        try:
            self._execute(*args, **kwargs)
        except Exception:
            self.retries += 1
            logger.exception('Task execute error, name = {}'.format(self.name))

            if self.retries > self.max_retries:
                raise Exception('Maximum number of retries exceeded')

    def _execute(self, *args, **kwargs):
        pass


def set_timeout(func, seconds: int):
    def runnable():
        time.sleep(seconds)
        func()

    concurrents.run_thread(runnable)
