# -*- coding: utf-8 -*-

import logging
import sys

import cliff
import cliff.app
from cliff.app import LOG
import cliff.commandmanager

import slapos.version

# silence messages like 'Starting connection' that are logged with INFO
urllib3_logger = logging.getLogger('requests.packages.urllib3')
urllib3_logger.setLevel(logging.WARNING)


class SlapOSCommandManager(cliff.commandmanager.CommandManager):

    def find_command(self, argv):
        """Given an argument list, find a command and
        return the processor and any remaining arguments.
        """
        # a little cheating, 'slapos node' is not documented by the help command
        if argv == ['node']:
            argv = ['node', 'status']

        search_args = argv[:]
        name = ''
        while search_args:
            if search_args[0].startswith('-'):
                raise ValueError('Invalid command %r' % search_args[0])
            next_val = search_args.pop(0)
            name = '%s %s' % (name, next_val) if name else next_val
            if name in self.commands:
                cmd_ep = self.commands[name]
                cmd_factory = cmd_ep.load()
                return (cmd_factory, name, search_args)
        else:
            print >>sys.stderr, ('The command %r does not exist or is not yet implemented.\n'
                                 'Please have a look at http://community.slapos.org to read documentation or forum.\n'
                                 'Please also make sure that SlapOS Node is up to date.' % (argv,))
            sys.exit(5)


class SlapOSApp(cliff.app.App):

    #
    # self.options.verbose_level:
    #    -q  -> 0 (WARNING)
    #        -> 1 (INFO)
    #    -v  -> 2 (DEBUG)
    #    -vv -> 3 (...)
    #    etc.
    #

    log = logging.getLogger('slapos')

    CONSOLE_MESSAGE_FORMAT = '%(message)s'
    LOG_FILE_MESSAGE_FORMAT = '[%(asctime)s] %(levelname)-8s %(name)s %(message)s'

    def __init__(self):
        super(SlapOSApp, self).__init__(
            description='SlapOS client %s' % slapos.version.version,
            version=slapos.version.version,
            command_manager=SlapOSCommandManager('slapos.cli'),
        )

    def build_option_parser(self, *args, **kw):
        kw.setdefault('argparse_kwargs', {})
        kw['argparse_kwargs']['conflict_handler'] = 'resolve'
        parser = super(SlapOSApp, self).build_option_parser(*args, **kw)

        # add two aliases for --log-file (for compatibility with old commands)
        parser.add_argument(
            '--log-file', '--logfile', '--log_file',
            action='store',
            default=None,
            help='Specify a file to log output (default: console only)',
        )

        return parser

    def initialize_app(self, argv):
        if self.options.verbose_level > 2:
            self.log.debug('initialize_app')

    def prepare_to_run_command(self, cmd):
        if self.options.verbose_level > 2:
            self.log.debug('prepare_to_run_command %s', cmd.__class__.__name__)

    def clean_up(self, cmd, result, err):
        if self.options.verbose_level > 2:
            self.log.debug('clean_up %s', cmd.__class__.__name__)

    def run(self, argv):
        # same as cliff.App.run except that it won't re-raise
        # a logged exception, and doesn't use --debug
        try:
            self.options, remainder = self.parser.parse_known_args(argv)
            self.configure_logging()
            self.interactive_mode = not remainder
            self.initialize_app(remainder)
        except Exception as err:
            LOG.exception(err)
            return 1
        if self.interactive_mode:
            result = self.interact()
        else:
            result = self.run_subcommand(remainder)
        return result

    def run_subcommand(self, argv):
        # same as cliff.App.run_subcommand except that it won't re-raise
        # a logged exception, and doesn't use --debug
        subcommand = self.command_manager.find_command(argv)
        cmd_factory, cmd_name, sub_argv = subcommand
        cmd = cmd_factory(self, self.options)
        err = None
        result = 1
        try:
            self.prepare_to_run_command(cmd)
            full_name = (cmd_name
                         if self.interactive_mode
                         else ' '.join([self.NAME, cmd_name])
                         )
            cmd_parser = cmd.get_parser(full_name)
            parsed_args = cmd_parser.parse_args(sub_argv)
            result = cmd.run(parsed_args)
        except Exception as err:
            LOG.exception(err)
            try:
                self.clean_up(cmd, result, err)
            except Exception as err2:
                LOG.exception(err2)
        else:
            try:
                self.clean_up(cmd, result, None)
            except Exception as err3:
                LOG.exception(err3)
        return result


def main(argv=sys.argv[1:]):
    app = SlapOSApp()
    if not argv:
        argv = ['-h']
    return app.run(argv)


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))