#!/usr/bin/env python3
#
# This file is part of OpenMediaVault.
#
# @license   https://www.gnu.org/licenses/gpl.html GPL Version 3
# @author    Volker Theile <volker.theile@openmediavault.org>
# @copyright Copyright (c) 2009-2017 Volker Theile
#
# OpenMediaVault is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# OpenMediaVault is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenMediaVault. If not, see <https://www.gnu.org/licenses/>.
import json
import re
import sys
from typing import List

import click
import natsort
import openmediavault.procutils
import openmediavault.settings

import openmediavault


def grep_env_vars() -> List[str]:
    result = set()
    # Get environment variables from `/etc/default/openmediavault`.
    result.update([
        key for key in openmediavault.settings.Environment.as_dict().keys()
        if key.startswith('OMV_')
    ])
    # Grep the environment variables used in Salt states.
    output = openmediavault.procutils.check_output(
        ['grep', '--no-filename', '--recursive', 'default:OMV_',
            openmediavault.getenv('OMV_SALT_DIR', '/srv/salt/omv')]
    )
    for line in output.splitlines():
        m = re.match(r".+'default:(OMV_[A-Z0-9_]+)'.+", line.decode())
        if not m:
            continue
        result.add(m.group(1))
    # Grep environment variables used in various shell scripts.
    paths = [
        '/etc/cron.daily',
        '/etc/cron.weekly',
        '/etc/cron.monthly',
        '/usr/sbin/omv-btrfs-scrub',
        '/usr/sbin/omv-sysinfo',
        '/usr/sbin/omv-upgrade'
    ]
    output = openmediavault.procutils.check_output(
        ['grep', '--no-filename', '--recursive', 'OMV_.*=${OMV_', *paths]
    )
    for line in output.splitlines():
        m = re.match(r"^(OMV_[a-zA-Z0-9_]+)=.+$", line.decode(), re.IGNORECASE)
        if not m:
            continue
        result.add(m.group(1))
    return natsort.humansorted(result)


class Context:
    verbose: bool


pass_context = click.make_pass_decorator(Context, ensure=True)


@click.group()
@click.option('-v', '--verbose', is_flag=True, help='Shows verbose output.')
@pass_context
def cli(ctx, verbose: bool):
    ctx.verbose = verbose
    return 0


@cli.command(name='list', help='List all available environment variables.')
@click.option('--json', 'json_output', is_flag=True,
              help='Print the output as JSON.')
@pass_context
def list_cmd(ctx, json_output: bool):  # noqa
    env_vars: List[str] = grep_env_vars()
    if json_output:
        print(json.dumps(env_vars))
    else:
        for env_var in env_vars:
            print(env_var)
    sys.exit(0)


@cli.command(name='get', help='Get configured environment variables.')
@click.argument('names', nargs=-1)
@click.option('--value-only', is_flag=True,
              help='Print only the value of the environment variable.')
@click.option('--json', 'json_output', is_flag=True,
              help='Print the output as JSON.')
@pass_context
def get_cmd(ctx, names: List[str], value_only: bool,
            json_output: bool):  # noqa
    if json_output and value_only:
        sys.stderr.write(
            'Options --json and --value-only are mutually exclusive\n')
        sys.exit(1)
    # Get all environment variables starting with 'OMV_'.
    env_vars = dict(filter(lambda item: item[0].startswith('OMV_'),
                           openmediavault.settings.Environment.as_dict().items()))
    # Check if we should display only given variables.
    if len(names) > 0:
        env_vars = dict(filter(lambda item: item[0] in names,
                               env_vars.items()))
        if len(env_vars) == 0:
            sys.exit(1)
    sorted_env_vars = natsort.humansorted(env_vars.items())
    if json_output:
        print(json.dumps(dict(sorted_env_vars)))
    else:
        for key, value in sorted_env_vars:
            print(value if value_only else '{}={}'.format(key, value))
    sys.exit(0)


@cli.command(name='set', help='Set an environment variable.')
@click.argument('key', required=True)
@click.argument('value', required=True)
@pass_context
def set_cmd(ctx, key: str, value: str):  # noqa
    if not key.startswith('OMV_'):
        sys.stderr.write('Key must be prefixed with OMV_\n')
        sys.exit(1)
    environment = openmediavault.settings.Environment
    environment.load()
    environment.set(key, value)
    environment.save()
    sys.exit(0)


@cli.command(name='unset', help='Unset an environment variable.')
@click.argument('key', required=True)
@pass_context
def unset_cmd(ctx, key: str):  # noqa
    if not key.startswith('OMV_'):
        sys.stderr.write('Key must be prefixed with OMV_\n')
        sys.exit(1)
    environment = openmediavault.settings.Environment
    environment.load()
    environment.unset(key)
    environment.save()
    sys.exit(0)


def main():
    cli()


if __name__ == '__main__':
    sys.exit(main())
