#!/usr/bin/env python
'''
A CLI interface to a remote salt-api instance
'''
from __future__ import print_function

import sys
import json
import logging

from upepper.cli import PepperCli
from upepper.retcode import PepperRetcode
from upepper.exceptions import (
    PepperException,
    PepperAuthException,
    PepperArgumentsException,
)

try:
    import salt.loader
    import salt.config
    import salt.output
    HAS_SALT = True
except ImportError:
    HAS_SALT = False

logger = logging.getLogger(__name__)


class Pepper(object):
    def __init__(self):
        self.cli = PepperCli()
        if HAS_SALT:
            self.opts = salt.config.client_config(self.cli.options.master)
        else:
            self.opts = {}
        if self.cli.options.output_file is not None:
            self.opts['output_file'] = self.cli.options.output_file

    @property
    def output(self):
        if not hasattr(self, 'modules'):
            self.modules = salt.loader.minion_mods(self.opts)
        try:
            oput = self.modules[self.cli.args[1]].__outputter__
        except (KeyError, AttributeError, TypeError):
            oput = 'nested'
        return oput

    def __call__(self):
        try:
            for exit_code, result in self.cli.run():
                if HAS_SALT and self.opts:
                    logger.debug('Use Salt outputters')
                    result = json.loads(result)

                    # unwrap ret in some cases
                    if 'return' in result:
                        result = result['return']

                    for ret in result:
                        if isinstance(ret, dict):
                            if self.cli.options.client.startswith('local'):
                                for minionid, minionret in ret.items():
                                    # rest_tornado doesnt return full_return directly
                                    # it will always be from get_event, so the output differs slightly
                                    if isinstance(minionret, dict) and 'return' in minionret:
                                        # version >= 2017.7
                                        salt.output.display_output(
                                            {minionid: minionret['return']},
                                            self.cli.options.output or minionret.get('out', None) or 'nested',
                                            opts=self.opts
                                        )
                                    # cherrypy returns with ret via full_return
                                    elif isinstance(minionret, dict) and 'ret' in minionret:
                                        # version >= 2017.7
                                        salt.output.display_output(
                                            {minionid: minionret['ret']},
                                            self.cli.options.output or minionret.get('out', None) or 'nested',
                                            opts=self.opts
                                        )
                                    else:
                                        salt.output.display_output(
                                            {minionid: minionret},
                                            self.cli.options.output or self.output,
                                            opts=self.opts
                                        )
                            elif 'data' in ret:
                                # unfold runners
                                outputter = ret.get('outputter', 'nested')
                                if isinstance(ret['data'], dict) and 'return' in ret['data']:
                                    ret = ret['data']['return']
                                salt.output.display_output(
                                    ret,
                                    self.cli.options.output or outputter,
                                    opts=self.opts
                                )
                            else:
                                salt.output.display_output(
                                    {self.cli.options.client: ret},
                                    self.cli.options.output or ret.get('outputter', 'nested'),
                                    opts=self.opts
                                )
                        else:
                            salt.output.display_output(
                                {self.cli.options.client: ret},
                                self.cli.options.output or 'nested',
                                opts=self.opts,
                            )
                else:
                    if self.cli.options.output_file is not None:
                        with open(self.cli.options.output_file, 'a') as ofile:
                            print(result, file=ofile)
                    else:
                        print(result)
                if exit_code is not None:
                    if exit_code == 0:
                        return PepperRetcode().validate(self.cli.options, result)
                    return exit_code
        except (PepperException, PepperAuthException, PepperArgumentsException) as exc:
            print('Pepper error: {0}'.format(exc), file=sys.stderr)
            return 1
        except KeyboardInterrupt:
            # TODO: mimic CLI and output JID on ctrl-c
            return 0
        except Exception as e:
            print(e)
            print('Uncaught Pepper error (increase verbosity for the full traceback).', file=sys.stderr)
            logger.debug('Uncaught traceback:', exc_info=True)
            return 1
