#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
utilitaire de lancement en fonction

- cron
- bareos

"""
import sys
import time
from os import readlink, unlink
from os.path import join
from glob import glob

from pyeole.process import system_out
from pyeole.schedule import SCHEDULE_DIR
from pyeole.bareos import is_running_jobs, bareos_get_jobs_list
from creole.client import CreoleClient
from pyeole.i18n import i18n
from pyeole.log import init_logging

_ = i18n('eole-schedule')

log = init_logging(name=u'eole-schedule', level='info', syslog=True,
                   console=['stdout', 'stderr'])

client = CreoleClient()

NOW = time.strftime('%d/%m/%y %H:%M', time.localtime())
# day in month
MONTH_DAY = int(time.strftime('%d', time.localtime()))
# day in week
WEEK_DAY = int(time.strftime('%w', time.localtime()))
HOUR = int(time.strftime('%H', time.localtime()))

# night start at 12
if HOUR > 12:
    WEEK_DAY += 1

if WEEK_DAY == 0:
    # sunday is 7
    WEEK_DAY = 7
if WEEK_DAY == 8:
    WEEK_DAY = 1


def log_parts(func):
    def split(msg):
        shreds = msg.split('\n')
        return [func(shred) for shred in shreds if len(shred) > 0]
    return split

log.info = log_parts(log.info)
log.error = log_parts(log.error)
log.warning = log_parts(log.warning)


def run_runparts(mode, bareos_type):
    """
    run part script
    test if weekly or monthly script must be launched this day
    """
    if mode == 'weekly' and WEEK_DAY != client.get('/schedule/schedule/weekday'):
        return
    if mode == 'monthly' and ( WEEK_DAY != client.get('/schedule/schedule/monthday') or MONTH_DAY > 7 ):
        return
    part_str = u"{} schedule {}".format(bareos_type, mode)
    log.info(_("Starting {}").format(part_str))
    dirname = join(SCHEDULE_DIR, mode, bareos_type)
    env = {'PATH': '/sbin:/usr/sbin:/bin:/usr/bin:/usr/share/eole',
           'LC_ALL': 'fr_FR.UTF-8'}
    if mode != 'once':
        runparts_cmd = "/bin/run-parts --exit-on-error -v --report {} --arg {} 2>&1"
        wrapped_runparts_cmd = ['/bin/bash',
                                '-c',
                                runparts_cmd.format(dirname, mode)]
        ret, out, err = system_out(wrapped_runparts_cmd, env=env)
    else:
        # unlink script before launch it
        # (for example remove 'reboot' link before restart the machine)
        names = glob(join(dirname, '*'))
        names.sort()
        ret = 0
        out = None
        err = None
        for name in names:
            if name.endswith('~'):
                continue
            script = readlink(name)
            unlink(name)
            wrapped_runparts_cmd = ['/bin/bash',
                                    '-c', script]
            ret, out, err = system_out(wrapped_runparts_cmd, env=env)
            if ret != 0:
                break
    if out:
        log.info(out)
    if err:
        log.error(err)
    if ret != 0:
        # on affiche sur stderr pour que cron le récupère et le mail
        # ce qui est printé sur stdout est envoyé dans les logs
        if out:
            sys.stderr.write(out)
        if err:
            sys.stderr.write(err)
        sys.stderr.write(_("Error detected\n"))
        log.error(_("{} exited with error return code").format(part_str))
        sys.exit(ret)
    log.info(_("{} finished").format(part_str))


def schedule_pre():
    run_runparts('daily', 'pre')
    run_runparts('weekly', 'pre')
    run_runparts('monthly', 'pre')
    run_runparts('once', 'pre')


def schedule_post():
    i = 0
    while is_running_jobs():
        time.sleep(1)
        i += 1
        if i == 30:
            log.info(_("Job already running, cancelling"))
            sys.exit(1)
    run_runparts('daily', 'post')
    run_runparts('weekly', 'post')
    run_runparts('monthly', 'post')
    run_runparts('once', 'post')


def schedule_cron():
    """
    If schedule.py is launched by cron, try to run pre and post
    cron file for daily, weekly and monthly if no backup is set this day
    """
    def exit_not_cron():
        log.info(_("bareos is set for this day, cancelled"))
        sys.exit(0)
    try:
        bareosjobs = bareos_get_jobs_list()
        for job in bareosjobs:
            day = int(job['day'])
            if job['job'] == 'daily':
                if day <= WEEK_DAY <= int(job['end_day']):
                    exit_not_cron()
            elif job['job'] == 'weekly':
                if WEEK_DAY == day:
                    exit_not_cron()
            elif job['job'] == 'monthly':
                if WEEK_DAY == day and MONTH_DAY < 8:
                    exit_not_cron()
            else:
                raise Exception(_('Unknown job: {0}').format(job['job']))
    except SystemExit:
        raise
    except:
        pass
    run_runparts('daily', 'pre')
    run_runparts('daily', 'post')

    run_runparts('weekly', 'pre')
    run_runparts('weekly', 'post')

    run_runparts('monthly', 'pre')
    run_runparts('monthly', 'post')

    run_runparts('once', 'pre')
    run_runparts('once', 'post')
# __________________________________________________


if __name__ == '__main__':

    usage = """usage:
{0} bareos [pre|post]
{0} cron""".format(sys.argv[0])

    if len(sys.argv) == 1:
        print(usage)
        sys.exit(1)

    if len(sys.argv) > 3:
        log.error(_("Too many arguments: {0}").format(sys.argv))
        print(usage)
        sys.exit(1)

    mode = sys.argv[1]

    if mode == 'bareos':
        # pre|post
        if len(sys.argv) == 2:
             log.error(_("Not enough arguments: {0}").format(sys.argv))
             print(usage)
             sys.exit(1)
        if sys.argv[2] not in ['pre', 'post']:
            log.error(_("Second argument must be pre or post: {0}").format(sys.argv))
            print(usage)
            sys.exit(1)
        bareos_type = sys.argv[2]
        if bareos_type == 'pre':
            schedule_pre()
        elif bareos_type == 'post':
            schedule_post()

    elif mode == 'cron':
        if len(sys.argv) != 2:
            log.error(_("Too many arguments for cron: {0}").format(sys.argv))
            print(usage)
            sys.exit(1)
        schedule_cron()
    else:
        log.error(_("Unknown schedule type : {0}").format(mode))
        print(usage)
        sys.exit(1)
