#!/usr/bin/python3 -d
"""
This file is part of Linspector (https://linspector.org/)
Copyright (c) 2013-2023 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE.
"""

# TODO: VERY IMPORTANT! IMPLEMENT ERROR HANDLING ALL THE WAY...!!!
#  Only exit execution on critical core errors. Do not exit when monitors are failing on
#  configuration and execution, but log these errors.
import argparse
import importlib
import signal
import sys

from loguru import logger as log

from linspector.configuration import Configuration
from linspector.environment import Environment
from linspector.linspector import Linspector
from linspector.monitors import Monitors

__version__ = '0.28.4'
__author__ = 'Johannes Findeisen <you@hanez.org>'

sys.stderr = open('/dev/null', 'w')


def parse_args():
    parser = argparse.ArgumentParser(
        description='linspector is a infrastructure and system monitoring daemon and toolchain.',
        epilog='author: ' + __author__,
        prog='linspector')

    parser.add_argument('configuration_path', metavar='CONFIGURATION_PATH',
                        help='the configuration path to use')

    parser.add_argument('-d', '--daemon', default=False, dest='daemon', action='store_true',
                        help='run linspector as native daemon (default: false)')

    parser.add_argument('-k', '--kill', default=False, dest='kill', action='store_true',
                        help='kill the daemon if it is running (default: false)')

    parser.add_argument('-r', '--restart', default=False, dest='restart', action='store_true',
                        help='restart the daemon if it is running (default: false)')

    parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + str(__version__))

    return parser.parse_args()


def linspector():
    print('Starting Linspector ' + __version__ + '...')
    print('This software is free software licensed under the terms of the MIT license. Copyright (c) 2013-2023 by ' +
          __author__)

    args = parse_args()
    monitors = {}
    notifications = {}
    plugins = {}
    # the scheduler is a dict to make it possible in the future to run more than one scheduler in
    # one linspector instance. need to think about it a little more...
    scheduler = {}
    services = {}
    tasks = {}

    try:
        configuration = Configuration(args.configuration_path)
    except Exception as err:
        print('Configuration error: {0}'.format(err))
        sys.exit(1)

    log.remove()
    default_log_format = 'timestamp={time:YYYY-MM-DD HH:mm:ss.SSSSSS ZZ} uptime={elapsed} level={level} ' \
                         'name=linspector {message}'

    if configuration.get_option('linspector', 'logfile'):
        logfile_count = '10'
        if configuration.get_option('linspector', 'logfile_count'):
            logfile_count = configuration.get_option('linspector', 'logfile_count')

        logfile_format = default_log_format
        if configuration.get_option('linspector', 'logfile_format'):
            logfile_format = configuration.get_option('linspector', 'logfile_format')

        logfile_level = 'INFO'
        if configuration.get_option('linspector', 'logfile_level'):
            logfile_level = configuration.get_option('linspector', 'logfile_level')
            if logfile_level == 'DEBUG':
                logfile_format = 'timestamp={time:YYYY-MM-DD HH:mm:ss.SSSSSS ZZ} uptime={elapsed} level={level} ' \
                                  'name=linspector exec={name}:{function} line={line} {message}'

        logfile_size = '1MB'
        if configuration.get_option('linspector', 'logfile_size'):
            logfile_size = configuration.get_option('linspector', 'logfile_size')

        log.add(configuration.get_option('linspector', 'logfile'), backtrace=True, diagnose=True, enqueue=True,
                format=logfile_format, level=logfile_level, retention=int(logfile_count), rotation=logfile_size)

    default_error_log_format = 'timestamp={time:YYYY-MM-DD HH:mm:ss.SSSSSS ZZ} uptime={elapsed} level={level} ' \
                               'name=linspector exec={name}:{function} line={line} {message}'

    if configuration.get_option('linspector', 'error_logfile'):
        error_logfile_count = '10'
        if configuration.get_option('linspector', 'error_logfile_count'):
            logfile_count = configuration.get_option('linspector', 'error_logfile_count')

        error_logfile_format = default_error_log_format
        if configuration.get_option('linspector', 'error_logfile_format'):
            logfile_format = configuration.get_option('linspector', 'error_logfile_format')

        error_logfile_level = 'ERROR'

        error_logfile_size = '1MB'
        if configuration.get_option('linspector', 'error_logfile_size'):
            error_logfile_size = configuration.get_option('linspector', 'error_logfile_size')

        log.add(configuration.get_option('linspector', 'error_logfile'), backtrace=True, diagnose=True,
                enqueue=True, format=error_logfile_format, level=error_logfile_level,
                retention=int(error_logfile_count), rotation=error_logfile_size)

    environment = Environment(log)

    try:
        if configuration.get_option('linspector', 'tasks'):
            log.info('loading tasks: ' + configuration.get_option('linspector', 'tasks'))
            print('Loading tasks: ' + configuration.get_option('linspector', 'tasks'))
            task_list = configuration.get_option('linspector', 'tasks')
            task_list = task_list.split(',')
            for task_name in task_list:
                task_package = 'linspector.tasks.' + task_name
                if importlib.util.find_spec(task_package) is not None:
                    task_module = importlib.import_module(task_package)
                    task_object = task_module.create(configuration, environment, log)
                    tasks[task_name] = task_object
                else:
                    log.warning('initialization of task: {0} failed! seems it does not exist.'.format(task_name))
    except Exception as err:
        log.warning('task initialization error: {0}'.format(err))

    try:
        monitors = Monitors(configuration, environment, log, notifications, services, tasks)
    except Exception as err:
        log.warning('monitor initialization error: {0}'.format(err))

    try:
        linspector = Linspector(configuration, environment, log, monitors, plugins, scheduler)
        # linspector.print_debug()
    except Exception as err:
        log.critical('core initialization error: {0}'.format(err))
        log.critical('program terminating...')
        sys.exit(1)

    # daemon initialization
    if args.daemon:
        try:
            from linspector.linspectord import Linspectord
            linspectord = Linspectord(configuration, environment, linspector, log)
            # do handling of restart, start and stop commands but for now "start" is enough... ;)
            if args.kill:
                log.info('stopping daemon.')
                linspectord.stop()
            elif args.restart:
                log.info('restarting daemon.')
                linspectord.restart()
            else:
                log.info('starting daemon.')
                linspectord.start()
        except Exception as err:
            log.critical('daemon error: {0}'.format(err))
            sys.exit(1)
    else:
        try:
            signal.pause()
        except KeyboardInterrupt:
            log.info('program terminated by user!')
            print('Program terminated by user!')


if __name__ == '__main__':
    linspector()
