#!/usr/bin/env python3

import  logging
logging.disable(logging.CRITICAL)

import  os
import  sys
import  json
import  socket
import  requests

import  pudb
import  pfmisc

# pfstorage local dependencies
from    pfmisc._colors      import  Colors
from    pfmisc.debug        import  debug
from    pfmisc.C_snode      import  *
from    pfstate             import  S

from    argparse            import RawTextHelpFormatter
from    argparse            import ArgumentParser

sys.path.insert(1, os.path.join(os.path.dirname(__file__), '..'))

from    chrisclient         import run

str_desc        = """

    NAME

        chrispl-run

    PREREQUISITES

        The python `pfmisc` and `pfstate` modules:

            pip install -U pfmisc pfstate

    SYNOPSIS

        chrispl-run             --plugin <someTemplateDesc>             \\
                                --args <pluginCLIargs>                  \\
                                [--across <metaSearchSpace>]            \\
                                [--onCUBE <CUBEjsonDetails>]            \\
                                [--onCUBEaddress <address>]             \\
                                [--version]                             \\
                                [--man]                                 \\
                                [--jsonReturn]                          \\
                                [--syslogPrepend]                       \\
                                [--verbosity <level>]

    ARGS

        --plugin <someTemplateDesc>
        The search condition/pattern to apply to the plugin space within
        CUBE. This should resolve to a *single* hit in the search space.

        The <someTemplateDesc> can be a compound string that is comma
        separated. For instance 'type=ds,name=pfdo' will search for all
        plugins that have 'pfdo' in their name and 'ds' as their type.

        Typical examples:

            --plugin name=pl-fshack
            --plugin name=dicom,type=ds

        Note that if multiple hits are resolved, all will be POSTed to
        CUBE with the same <pluginCLIargs>. In most cases this will
        probably NOT be desired.

        --args <pluginCLIargs>
        A comma separated list of CLI arguments that are relevant to the
        plugin being executed. These arguments are appropriately parsed
        and POSTed to the backend.

        [--onCUBE <CUBEjsonDetails>]
        A JSON string that defines detail perinent to CUBE. If not passed,
        will use defaults. If passed, make sure to contain all the following:

            --onCUBE '
            {
                "protocol":     "http",
                "port":         "8000",
                "address":      "%HOSTIP",
                "user":         "chris",
                "password":     "chris1234",
            }
            '

        [--onCUBEaddress <address>]
        Instead of passing a complete JSON object as with ``--onCUBE``, in
        some cases only the address (IP or name) of the ``CUBE`` instance
        needs to be set. This flag is a mechanism to only set the address
        of the target ``CUBE``.

        [--across <metaSearchSpace>]
        An optional qualifier that defines a "meta" search space. This is
        provided to allow for correct instantiation of the internal search
        module and should not be explicitly set at the CLI level!

        [--version]
        Print the version and exit.

        [--jsonReturn]
        If specified, print the full JSON return from the API call and
        various class methods that apply processing to the API call.

        [--syslogPrepend]
        If specified, prepend pseudo (colorized) syslog info to output
        print calls.

        [--verbosity <level>]
        Apply a verbosity to the output.


    DESCRIPTION

        ``chrispl-run`` provides for a simple CLI mechanism to schedule a ChRIS
        plugin via CUBE in a manner that is roughly analogous to the plugin
        CLI interface.

        The primary purpose of this script is to easily execute a plugin
        via CUBE. When scheduled, the script will return the instance ID
        of the executed plugin in the CLI -- this CLI string might need to
        be parsed since the actual ID is the third word on the CLI return.

        If a plugin is connected to a parent node, provide the plugin instance
        id in the ``--args`` string as ``previous_id=<N>`` where <N> is the
        specific instance ID.

    EXAMPLES

    Run an instance of the ``pl-freesurfer_pp`` plugin and connect to
    upstream node of instance ID ``1``:

        $ chrispl-run    --plugin name=freesurfer_pp         \\
                        --args="--ageSpec=10-06-01;          \\
                        --copySpec=sag,cor,tra,stats,3D;     \\
                        --previous_id=1"

    which should return

        (name=freesurfer_pp)        id 12

    Indicating the ID of this plugin instance.


"""

# Determine the hostIP
str_defIP   = [l for l in (
                [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2]
                if not ip.startswith("127.")][:1],
                    [[(s.connect(('8.8.8.8', 53)),
                       s.getsockname()[0], s.close())
                for s in [socket.socket(socket.AF_INET,
                          socket.SOCK_DGRAM)]][0][1]]) if l][0][0]


str_version     = "1.2.8"
str_name        = "chrispl-run"
parser          = ArgumentParser(
                    description     = str_desc,
                    formatter_class = RawTextHelpFormatter
)

parser.add_argument(
    '--onCUBE',
    help    = 'A JSON string defining the details of a CUBE instance',
    action  = 'store',
    dest    = 'str_CUBE',
    default = '',
)
parser.add_argument(
    '--onCUBEaddress',
    help    = 'the address of a CUBE instance',
    action  = 'store',
    dest    = 'str_CUBEaddress',
    default = '',
)
parser.add_argument(
    '--version',
    help    = 'if specified, print verion',
    action  = 'store_true',
    dest    = 'b_version',
    default = False,
)
parser.add_argument(
    '--man', '-x',
    help    = 'if specified, show help and exit',
    action  = 'store_true',
    dest    = 'b_man',
    default = False,
)
parser.add_argument(
    '--syslogPrepend',
    help    = 'if specified, prepend syslog info to output',
    action  = 'store_true',
    dest    = 'b_syslog',
    default = False,
)
parser.add_argument(
    '--jsonReturn',
    help    = 'if specified, return results as JSON',
    action  = 'store_true',
    dest    = 'b_json',
    default = False,
)
parser.add_argument(
    '--plugin',
    help    = 'a plugin spec to parse and schedule',
    action  = 'store',
    dest    = 'str_pluginSpec',
    default = '',
)
parser.add_argument(
    '--args',
    help    = 'a comma separated CLI list of args to pass to the plugin',
    action  = 'store',
    dest    = 'str_args',
    default = '',
)
parser.add_argument(
    '--across',
    help    = 'a metaspace across which to search (files, instances, etc)',
    action  = 'store',
    dest    = 'str_across',
    default = 'plugins',
)
parser.add_argument(
    '--verbosity',
    help    = 'the system verbosity',
    action  = 'store',
    dest    = 'verbosity',
    default = 1,
)

args        = parser.parse_args()

def preprocessing_do(*args):
    """
    Some quick preprocessing (check for --man or --version flags)
    """
    d_args  = vars(args[0])
    if d_args['b_man']:
        print(str_desc)
        sys.exit(0)

    if d_args['b_version']:
        print(str_version)
        sys.exit(0)

def postprocessing_do(schedule, d_result):
    """
    Final postprocessing housekeeping.
    """
    retCode     :   int      = 1
    if schedule.d_args['b_json']:
        # print(json.dumps(d_result, indent =4))
        schedule.dp.qprint(json.dumps(d_result, indent = 4))
        if 'target' in d_result['run']:
            if len(d_result['run']['target']):
                retCode = 0
    else:
        if d_result['status']:
            for target in d_result['run']['target']:
                schedule.dp.qprint('(%s)' % schedule.d_args['str_using'], end='')
                for hit in target:
                    schedule.dp.qprint('%10s %-30s' % (hit['name'], hit['value']),
                                    end='', syslog=False)
                schedule.dp.qprint('')
                retCode = 0
        else:
            schedule.dp.qprint("Plugin run failed", comms = 'error')
    return retCode

def main(*args):
    """
    The main method of the script, when called directly from the CLI
    """
    retCode     : int   = 1
    preprocessing_do(*args)

    d_meta      : dict  = {
        'version':  str_version,
        'name':     str_name,
        'desc':     str_desc,
        'defIP':    str_defIP
    }

    schedule    = run.PluginRun(d_meta, args[0])
    d_result    = schedule.do()
    retCode     = postprocessing_do(schedule, d_result)

    sys.exit(retCode)

if __name__ == "__main__":
    main(args)
