#!/usr/bin/python3.13 -s

# prep for python 3
from __future__ import print_function

# requires python2-requests/python3-requests

import logging
import os
import sys
import argparse
import signal

from pyparsing import (alphanums, OneOrMore, Optional, Regex, Suppress, Word)

from configshell_fb import ConfigShell, ExecutionError
from configshell_fb.shell import locatedExpr
from gwcli.gateway import ISCSIRoot

import ceph_iscsi_config.settings as settings

__author__ = 'Paul Cuzner'
__version__ = '2.7'


class GatewayCLI(ConfigShell):

    default_prefs = {'color_path': 'magenta',
                     'color_command': 'cyan',
                     'color_parameter': 'magenta',
                     'color_keyword': 'cyan',
                     'completions_in_columns': True,
                     'logfile': None,
                     'loglevel_console': 'info',
                     'loglevel_file': 'debug9',
                     'color_mode': True,
                     'prompt_length': 30,
                     'tree_max_depth': 0,
                     'tree_status_mode': True,
                     'tree_round_nodes': True,
                     'tree_show_root': True,
                     }


    def __init__(self, preferences_dir=None):
        super(GatewayCLI, self).__init__(preferences_dir)
        # Grammar of the command line
        command = locatedExpr(Word(alphanums + '_'))('command')
        var = Word(alphanums + ',=_\+/.<>()~@:-%[]')
        value = var
        keyword = Word(alphanums + '_\-')
        kparam = locatedExpr(keyword + Suppress('=') + Optional(value, default=''))('kparams*')
        pparam = locatedExpr(var)('pparams*')
        parameter = kparam | pparam
        parameters = OneOrMore(parameter)
        bookmark = Regex('@([A-Za-z0-9:_.]|-)+')
        pathstd = Regex('([A-Za-z0-9:_.\[\]]|-)*' + '/' + '([A-Za-z0-9:_.\[\]/]|-)*') \
                | '..' | '.'
        path = locatedExpr(bookmark | pathstd | '*')('path')
        parser = Optional(path) + Optional(command) + Optional(parameters)
        self._parser = parser


def exception_handler(exception_type, exception, traceback,
                      debug_hook=sys.excepthook):

    if options.debug:
        debug_hook(exception_type, exception, traceback)
    else:
        color_red = '\x1b[31;1m'
        color_off = '\x1b[0m'
        print("{}{}: {}{}".format(color_red, exception_type.__name__,
                                  exception, color_off))


def get_options():

    # Set up the runtime overrides, any of these could be provided
    # by the cfg file(s)
    parser = argparse.ArgumentParser(prog='gwcli',
                                     description='Manage iSCSI gateways')
    parser.add_argument('-c', '--config-object', type=str,
                        help='pool and object name holding the iSCSI config'
                             ' object (pool/object_name)')
    parser.add_argument('-d', '--debug', action='store_true',
                        default=False,
                        help='run with additional debug')
    parser.add_argument('-t', '--threads', type=int,
                        default=8,
                        help='threads used for rbd scanning (default is 8)')
    parser.add_argument('-v', '--version', action='version',
                        version='%(prog)s - {}'.format(__version__))
    parser.add_argument('cli_command', type=str, nargs=argparse.REMAINDER)

    # create the opts object
    opts = parser.parse_args()

    # establish defaults, just in case they're missing from the config
    # file(s) AND run time call
    if not opts.config_object:
        opts.config_object = 'rbd/gateway.conf'

    opts.cli_command = ' '.join(opts.cli_command)

    return opts


def kbd_handler(*args):
    pass


def main():
    is_root = True if os.getuid() == 0 else False
    if not is_root:
        print("CLI only supports root level access")
        sys.exit(-1)

    shell = GatewayCLI('~/.gwcli')

    root_node = ISCSIRoot(shell,
                          scan_threads=options.threads)

    root_node.interactive = False if options.cli_command else True
    settings.config.interactive = False if options.cli_command else True

    # Load the config to populate the object model
    root_node.refresh()
    if root_node.error:
        print("Unable to contact the local API endpoint "
              "({})".format(settings.config.api_endpoint))
        sys.exit(-1)

    # Account for invocation which includes a command to run i.e. batch mode
    if options.cli_command:

        try:
            shell.run_cmdline(options.cli_command)
        except Exception as e:
            print(str(e), file=sys.stderr)
            sys.exit(-1)

        sys.exit(0)

    # Main loop - run the interactive shell, until the user exits
    while not shell._exit:
        try:
            shell.run_interactive()
        except ExecutionError as msg:
            shell.log.error(str(msg))


def log_in_color(fn):

    def new(*args):
        colour_off = '\x1b[0m'
        levelno = args[0].levelno

        if levelno >= logging.CRITICAL:
            color = '\x1b[31;1m'
        elif levelno >= logging.ERROR:
            color = '\x1b[31;1m'
        elif levelno >= logging.WARNING:
            color = '\x1b[33;1m'
        elif levelno >= logging.INFO:
            color = '\x1b[32;1m'
        elif levelno >= logging.DEBUG:
            color = '\x1b[34;1m'
        else:
            color = '\x1b[0m'

        args[0].msg = "{}{}{}".format(color, args[0].msg, colour_off)

        return fn(*args)
    return new


if __name__ == "__main__":
    options = get_options()

    # Setup logging
    log_path = os.path.join(os.path.expanduser("~"), "gwcli.log")

    logger = logging.getLogger('gwcli')
    logger.setLevel(logging.DEBUG)

    file_handler = logging.FileHandler(log_path, mode='a')

    file_format = logging.Formatter('%(asctime)s %(levelname)-8s '
                                    '[%(filename)s:%(lineno)s:%(funcName)s()]'
                                    ' %(message)s')

    file_handler.setFormatter(file_format)
    file_handler.setLevel(logging.DEBUG)
    logger.addHandler(file_handler)

    if not options.cli_command:
        stream_handler = logging.StreamHandler(stream=sys.stdout)
        if options.debug:
            stream_handler.setLevel(logging.DEBUG)
        else:
            stream_handler.setLevel(logging.INFO)

        stream_handler.emit = log_in_color(stream_handler.emit)
        logger.addHandler(stream_handler)

    # Override the default exception handler to only show back traces
    # in debug mode
    sys.excepthook = exception_handler

    # Intercept ctrl-c and ctrl-z events to stop the user exiting
    signal.signal(signal.SIGTSTP, kbd_handler)
    signal.signal(signal.SIGINT, kbd_handler)

    settings.init()

    main()
