#!/usr/bin/env python3
import subprocess
import os
import argparse
from argparse import RawTextHelpFormatter
from logger_slg import init_logger
import yaml
from pprint import pformat

REQUIRED_ARGUMENTS = ['username']
DYNAMIC_DEFAULTS = { # If an argument is defaulted to a derivative of a previous required argument, we can set a lambda of said required argument
    'timelog_config_filepath': lambda username: f'/home/{username}/.config/slg/time_log.yml',
    'time_log_filepath': lambda username: f'/home/{username}/timetrack-slg/time-log.json'
}
SENSITIVE_ARGUMENTS = [] # place any passwords or other sensitive arguments in here to not expose them in configuration printing
SPECIAL_REQUIREMENTS = { # variable must return a truthy value for these lambda functions in order to proceed with running the script; follow the example requirements precedence for setting new requirements
    # 'config_filepath': [
    #     {
    #         'name': 'Filepath is an absolute filepath',
    #         'requirement': lambda value: value.startswith('/')
    #     },
    # ]
}


def get_arguments():
    parser = argparse.ArgumentParser(description='', formatter_class=RawTextHelpFormatter)

    parser.add_argument('-u', '--username', help='Username to run this command as')

    parser.add_argument('-c', '--timelog_config_filepath', help='Path to configuration file. Default: /home/$(whoami)/.config/slg/time_log.yml')

    parser.add_argument('-t', '--time_log_filepath', help='Where to write our timelog file. Must be .json file. Default: /home/$(whoami)/timetrack-slg/time-log.json')

    args = parser.parse_args()

    return args


def get_dynamic_default_arg(args, desired_arg):
    parameters = DYNAMIC_DEFAULTS[desired_arg].__code__.co_varnames
    parameter_values = [args[arg] for arg in parameters]
    return DYNAMIC_DEFAULTS[desired_arg](*parameter_values)

def build_true_configuration(args, config_filepath=None):
    # arguments defined at the command line take precedence over config file variables
    config = {}
    if config_filepath:
        config = read_config_file(config_filepath)

    dict_args = args.__dict__
    for arg in dict_args:
        if dict_args[arg] is not None:
            config[arg] = dict_args[arg]

    # handle for dynamic defaults if no value is set
    for arg in DYNAMIC_DEFAULTS:
        if not config.get(arg):
            config[arg] = get_dynamic_default_arg(config, arg)

    return config

def strip_sensitive_arguments(config):
    return {k: v for k, v in config.items() if k not in SENSITIVE_ARGUMENTS}

def guarantee_requirements_met(config):
    # config is the config object after assigning arg values to the config file values

    # first iterate over required arguments
    for argument in REQUIRED_ARGUMENTS:
        if not config.get(argument):
            logger.error(f'\n\nRequired argument "{argument}" not found. Exiting...')
            exit(0)

    # then iterate over the special requirements
    for argument in SPECIAL_REQUIREMENTS:
        value = config.get(argument)
        for requirement_obj in SPECIAL_REQUIREMENTS[argument]:
            if not requirement_obj['requirement'](value):
                logger.error(f'\n\nSpecial requirement "{requirement_obj["name"]}" was not met for the argument "{argument}". Exiting...')
                exit(0)

def read_config_file(filepath):
    try:
        with open(filepath, 'r') as stream:
            try:
                config = yaml.safe_load(stream)
                return config
            except yaml.YAMLError as exc:
                print(exc)
                logger.error('\n\nYAML Error. Exiting...')
                exit(0)
    except FileNotFoundError:
        logger.exception('Config file not found. Proceeding with defaults')
        return {}

def is_root():
    return os.geteuid() == 0

def confirm_output(command, expected_outputs=[lambda output: output == ''], print_output=False):
    output = subprocess.check_output(command, shell=True).decode('utf-8').strip()
    if print_output:
        print(output)
    for func in expected_outputs:
        if func(output) == True:
            return True
    print(f'''\nCommand:

{command}

had the unanticipated output of:

{output}

Please review any potential consequences of this.
''')



SYSTEMD_SERVICE = lambda username, display_var, path_to_script, time_log_filepath, config_filepath: f'''
[Unit]
Description=Time tracker
After=multi-user.target

[Service]
User={username}
Type=simple
Restart=always
Environment="{display_var}"
ExecStart={path_to_script} --output_filepath {time_log_filepath} --sleep_time 0.96 --time_til_idle 30 --config_filepath {config_filepath}

[Install]
WantedBy=multi-user.target
'''

DEFAULT_CONFIG = '''
# DynamicPageTitles:
    # 'static part of dynamic title': 'resolved string'

# OverrideAll:
    # 'Twitch -': true

IdleIgnore:
    'Twitch -': true
    '- YouTube -': true
    'Podcast on Spotify': true
'''

if __name__ == '__main__':
    args = get_arguments()

    if not is_root():
        print(f'\nScript needs to be run as root: sudo {__file__.split("/")[-1]} <options>\n')
        print('Steps taken can be seen in the getting started section at https://github.com/slgotting/timetrack-slg\n')
        print(f'See the contents of this script by running: cat $(whereis {__file__.split("/")[-1]})\n')
        exit(0)

    try:
        config = build_true_configuration(args)
        guarantee_requirements_met(config)

        username = config.get('username')
        config_filepath = config.get('timelog_config_filepath')
        time_log_filepath = config.get('time_log_filepath')

        # create log directory / handle ownership
        confirm_output(f"sudo mkdir -p /var/log/slg")
        confirm_output(f"sudo chown {username}:{username} /var/log/slg")

        logger = init_logger(
            name=__name__,
            log_path=f'/var/log/slg/{__file__.split("/")[-1]}.log'
        )
        sensitive_stripped_config = strip_sensitive_arguments(config)
        logger.info(f'\nUsing configuration:\n\n{pformat(sensitive_stripped_config)}')

        # create config directory and file / handle ownership
        confirm_output(f"sudo mkdir -p {'/'.join(config_filepath.split('/')[:-1])} && sudo echo \"{DEFAULT_CONFIG}\" > {config_filepath}")
        confirm_output(f"sudo chown {username}:{username} {'/'.join(config_filepath.split('/')[:-1])}")
        confirm_output(f"sudo chown {username}:{username} {config_filepath}")

        display_var = subprocess.check_output(f"env | grep DISPLAY", shell=True).decode('utf-8').strip()
        # path_to_script = subprocess.check_output(f"whereis timetrack-slg").decode('utf-8').strip()
        path_to_script = f'/home/{username}/.local/bin/timetrack-slg'
        confirm_output(f"sudo echo \"{SYSTEMD_SERVICE(username, display_var, path_to_script, time_log_filepath, config_filepath)}\" > /etc/systemd/system/timetrack-slg.service")

        # setup the systemctl service
        confirm_output(f"sudo systemctl daemon-reload")
        confirm_output(f"sudo systemctl enable timetrack-slg.service", expected_outputs=[lambda output: output == '', lambda output: output.startswith('Created symlink')], print_output=True)
        confirm_output(f"sudo systemctl start timetrack-slg.service")

        # install new crontab line
        path_to_consolidate_script = f'/home/{username}/.local/bin/timetrack-consolidate-slg'
        crontab_addition = f'\n\n*/5 * * * * {path_to_consolidate_script} --input_filepath {time_log_filepath} --config_filepath {config_filepath} --run_interval 5'
        confirm_output(f'(crontab -u {username} -l; echo \'{crontab_addition}\' ) | crontab -u {username} -', expected_outputs=[lambda output: output == '', lambda output: output.startswith('No crontab')])

        print('\n\nInstallation successful.\n')
        print(f'Find your time log file at {time_log_filepath}\n')

    except:
        logger.exception('An error occurred')
