'''The client side of the multi_facility_process_helper.
Mostly a utility class with a lot of convenience methods.'''
from __future__ import print_function

import sys
import os.path
import json
import subprocess
import socket
import traceback
import globals
import toolconfig


def executeCommandsOnFacility(facility, commandsAndArgs):
    '''Executes a list of commands on the specified facility using ssh and the multi_facility_process_helper.
    facility - This is the facility name.
    commandsAndArgs - A list of tuples - each tuple has two components. First component is a string command. Second command is a jsonable arg. Both are mandatory.
    We return the deserialized contents of executing the set of commands on the remote facility as a list of objects
    '''
    facilityConfig = toolconfig.getConfigForFacility(facility)
    # Each facility has a config element that tells us machine and use credentials.
    ssh_config = facilityConfig['ssh']

    # This field can be a string or a list of strings in order of preference
    # If it is a string, convert it to a list
    if type(ssh_config) is not list:
        ssh_list = [ssh_config]
    else:
        ssh_list = ssh_config

    # Iterate through ssh login strings and return on success
    for i, ssh in enumerate(ssh_list):
        if globals.verbose:
            print("Found ssh config {0}".format(ssh))

        # If destination facility is the same as the origin, do not use ssh
        destHost = ssh.split('@')[-1]
        origHost = socket.gethostname()
        mfHelperPath = 'multi_facility_process_helper'
        if destHost == origHost:
           if globals.verbose:
               print("Destination and origin facilities are the same, no ssh needed")
           conargs=[mfHelperPath]
        elif toolconfig.usesGateway(facility):
           gateway = facilityConfig['gateway']
           conargs=['ssh','-x', gateway,'ssh -x -o BatchMode=yes ' ,ssh, mfHelperPath]
        else:
           conargs=['ssh','-x',ssh,'-o BatchMode=yes', mfHelperPath]

        if globals.verbose:
            conargs.append("-v")

        try:
            proc = subprocess.Popen(conargs,
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
            commandStr = json.dumps(commandsAndArgs)
            if globals.verbose:
                if destHost == origHost:
                    print("Sending to multi_facility_process_helper - {0}".format(commandStr))
                else:
                    print("Sending to remote multi_facility_process_helper - {0} on {1} with ssh = {2} using {3} gateway".format(commandStr, facility, ssh, gateway if toolconfig.usesGateway(facility) else ' no '))

            [stdoutdata, stderrdata] = proc.communicate(commandStr.encode('utf-8'))
            stdoutdata = stdoutdata.decode('utf-8')
            stderrdata = stderrdata.decode('utf-8')

            if globals.verbose:
                print("stderr {0}".format(stderrdata))
            if globals.verbose:
                print("stdout {0}".format(stdoutdata))

            if proc.returncode != 0:
               err_msg = {'name':facility,'stdout':stdoutdata,'stderr':stderrdata,'rc':proc.returncode}
               raise connectionException(err_msg)
            try:
                ret = json.loads(stdoutdata)
            except ValueError as e:
                print("stderr {0}".format(stderrdata))
                print("stdout {0}".format(stdoutdata))
                raise e
            if globals.verbose:
                print("Return from multi_facility_process_helper {0}".format(ret))
            return ret

        except (connectionException, ValueError) as e:
            # If no more ssh logins left, reraise exception
            if i == len(ssh_list) - 1:
                raise e

            # Otherwise, print exception but allow loop to continue
            if globals.verbose:
                traceback.print_exc(file=sys.stdout)

        except subprocess.CalledProcessError:
            return ""



class connectionException(SystemExit):
    '''A wrapper for exceptions that occur on the destination facility. It inherits SystemExit so that no traceback is printed.
    data - A dict that encapsulates the status of multi_facility_process_helper at the time of exception
    If verbose is not enabled, only print the facility and error message. Otherwise, print the facility, stdout, stderr, and return code
    '''
    def __init__(self, data):
        if globals.verbose:
            message = data
        else:
            # Insert the facility name before the colon in "Exception:"
            message = data['stderr'].replace(":", " in facility {0}:".format(data['name']))

        super(connectionException, self).__init__(message)
