#!/usr/bin/env python3

# Copyright (C) 2020  Braiins Systems s.r.o.
#
# This file is part of Braiins Open-Source Initiative (BOSI).
#
# BOSI 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
# (at your option) any later version.
#
# This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
#
# Please, keep in mind that we may also license BOSI or any part thereof
# under a proprietary license. For more information on the terms and conditions
# of such proprietary license or if you have any other questions, please
# contact us at opensource@braiins.com.

import argparse
import sys
import warnings
import logging

from functools import partial

from . import __version__
from .commands import unlock, install, uninstall, discover, command, config, update
from .common import ToolboxError
from .platform import PlatformStop

from bos_utils.ssh import SSHError

LOG = logging.getLogger(__name__)


def add_tool(subparsers, tool):
    """
    Register a new tool into the toolbox
    :param tool: tuple containing tool name used as subcommand and the actual tool module
    :return:
    """
    (name, module) = tool
    parser = subparsers.add_parser(name)
    parser.set_defaults(func=partial(module.main, parser))
    module.build_arg_parser(parser)


def get_version():
    try:
        import version

        return version.toolbox
    except ModuleNotFoundError:
        return 'devel'


def main():
    # rid of useless warning from paramiko
    warnings.filterwarnings('ignore', category=UserWarning, module='paramiko')
    warnings.filterwarnings('ignore', category=DeprecationWarning, module='paramiko')
    # silence cryptography deprecation warning from asyncssh in discover
    warnings.filterwarnings('ignore', module='asyncssh')
    # asyncio have spam problem on windows
    logging.getLogger('asyncio').disabled = True

    # execute only if run as a script
    parser = argparse.ArgumentParser(
        description='Provides tools for managing mining '
        'devices running Braiins OS and '
        'Braiins OS+'
    )
    parser.add_argument(
        '--version', action='version', version='%(prog)s {}'.format(__version__)
    )
    parser.add_argument(
        '-v', '--verbose', help='increase output verbosity', action='store_true'
    )
    parser.add_argument(
        '--referral-id-from-dir', help=argparse.SUPPRESS, action='store_true'
    )
    subparsers = parser.add_subparsers(
        title='subcommands',
        description='Braiins OS tools in the ' 'toolbox',
        help='Choose one of the tools and run '
        'with --help to retrieve additional info '
        'how to use it',
    )

    for t in [
        ('unlock', unlock),
        ('install', install),
        ('uninstall', uninstall),
        ('discover', discover),
        ('command', command),
        ('config', config),
        ('update', update),
    ]:
        add_tool(subparsers, t)

    # parse command line arguments
    args = parser.parse_args(sys.argv[1:])

    if args.verbose:
        logging_level = logging.DEBUG
        logging_format = '[%(asctime)s] %(levelname)s:%(name)s:%(message)s'
    else:
        logging_level = logging.INFO
        logging_format = '[%(asctime)s] %(levelname)s:%(message)s'

    logging_datefmt = '%Y-%m-%d %H:%M:%S'
    logging.basicConfig(
        format=logging_format, datefmt=logging_datefmt, level=logging_level
    )

    # Python3 workaround https://bugs.python.org/issue16308
    try:
        func = args.func
    except AttributeError:
        parser.print_help(sys.stderr)
        import platform

        if platform.system() == 'Windows':
            input('Press Enter to continue...')
        sys.exit(1)

    try:
        args.func(args)
    except KeyboardInterrupt:
        print()
        sys.exit(1)
    except SSHError as e:
        LOG.error(str(e))
        sys.exit(1)
    except ToolboxError as ex:
        LOG.error(ex)
        for ctx_ex in ex.context():
            LOG.error(f'- {ctx_ex}')
        sys.exit(2)
    except unlock.UnlockStop:
        sys.exit(2)
    except install.UpgradeStop:
        sys.exit(2)
    except uninstall.RestoreStop:
        sys.exit(3)
    except update.UpdateStop:
        sys.exit(4)
    except PlatformStop:
        sys.exit(5)
    except OSError as ex:
        LOG.error(ex)
        sys.exit(6)
    except Exception as ex:
        config.log_error()
        LOG.exception(ex)
        sys.exit(7)


if __name__ == '__main__':
    main()
