#!/usr/bin/env python
'''
This utility is intended to support operators reverting releases for IOC's and other crammed packages.
We expect to know the package name, type and IOC name as arguments.
Unlike other cram utilities, this utility is expected to run in the facility containing the IOC (that is, it is expected to be run by the operators).
So, we do not make an ssh call thru mcclogin and instead call multi_facility_process_helper.py directly.
We get the history of upgrades to this IOC/app and display that in a list.
Operator chooses one of these (or can cancel the operation); we revert the IOC to the version chosen by the operator.
Operator can then reboot the IOC for the changes to take effect.
'''
from __future__ import print_function

import os
import sys
import argparse
import subprocess
import json
import globals
import toolconfig
import multi_facility_process_helper
import getpass
from globals import UserParamsException

def findFileInPath(fileName, path=None):
    ''' Find fileName in the specified path; use $PATH if path is not specified'''
    path = path or os.environ['PATH']
    for dir in path.split(os.pathsep):
        filepath = os.path.join(dir, fileName)
        if os.path.exists(filepath):
            return filepath
    return None

def determineCurrentFacility():
    cram_facility = findFileInPath("cram_facility")
    if not cram_facility:
        print("Cannot determine the facility using the file cram_facility.")
        sys.exit(-1)
    if globals.verbose:
        print("Determining current facility using {0}".format(cram_facility))
    with open(cram_facility, 'r') as f:
        lines = f.readlines()
        for line in lines:
            if line.startswith("#"):
                continue
            facility = line.strip().split()[0]
            return facility
    return None
        


def execute(args):
    parser = argparse.ArgumentParser(prog="operator_revert_release", description="Used by operators to revert a IOC/HLA/other instance to a previous release.")
    parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose logging')
    parser.add_argument('-n', '--name', help='Specify the application name; as we expect the operators to be running this, we cannot expect to pick up this information from the .cram/packageinfo', required=True)
    parser.add_argument('-t', '--type', help='Specifies the type of application from the deployment perspective. Possible values are ' + ", ".join(toolconfig.appTypes.keys()), required=True)
    parser.add_argument('-i', '--ioc', help='Specify the IOC if this is an HIOC or an SIOC package. This is mandatory for IOC packages.')
    args = parser.parse_args(args)
    globals.verbose = args.verbose

    packageInfo = toolconfig.determinePackageInfoFromArgs(args)
    name = packageInfo['name']
    type = packageInfo['type']
    # For this utility, we determine the current facility using the cram_facility softlink.
    facility = determineCurrentFacility()
    if not facility:
        print("Cannot determine the facility using the file cram_facility.")
        sys.exit(-1)
    if globals.verbose:
        print("Determined facility as {0}".format(facility))
    
    # Now that we know the package name and type, determine the history of upgrades.
    histargs = {}
    histargs['name'] = name
    histargs['type'] = type
    facilityConfig = toolconfig.getConfigForFacility(facility)
    if type not in facilityConfig:
        print("Type {0} does not exist for facility {1} in the global facility config".format(type, facility))
        sys.exit(-1)

    histargs['releaseFolder'] = toolconfig.getConfigForFacility(facility)[type]['releaseFolder']
    versions = multi_facility_process_helper.hist(histargs)
    if globals.verbose:
        print("History for package is {0}".format(versions))
    
    if type in ["HIOC", "SIOC"]:
        if 'ioc' not in args or not args.ioc or args.ioc == 'ALL':
            print("This utility needs to know the name of the specific IOC ")
            sys.exit(-1)
        ioc = args.ioc

        if ioc not in versions:
            subprocess.call(['zenity', '--info', '--text=The ioc ' + ioc + ' does not have any history in this facility.'])
            sys.exit(-1)

        # The first item in the history is the current release. For usability, we remove this.
        ioc_versions = versions[ioc][1:]

        if globals.verbose:
            print("History of ioc {0} is {1}".format(ioc, ioc_versions))
        
        versions_for_zenity = []

        for version in ioc_versions:
            versions_for_zenity.append(version['release'])
            versions_for_zenity.append(version['datetime'])
            versions_for_zenity.append(version['user'])
            
        if not versions_for_zenity:
            subprocess.call(['zenity', '--info', '--text=The ioc ' + ioc + ' does not have any history in this facility.'])
            sys.exit(-1)
            
        if globals.verbose:
            print(versions_for_zenity)
        # Pop up a zenity list dialog with the releases
        zenity_args = ['zenity', '--list',
                                 '--title', 'Choose the previous version of the package {0} that you want the IOC {1} to revert to'.format(name, ioc),
                                 '--width=500', '--height=600',
                                 '--column=Version', '--column=Date', '--column=User']
        zenity_args.extend(versions_for_zenity)
        try:
            user_choice = subprocess.check_output(zenity_args).decode('utf-8').strip()
            if globals.verbose:
                print("User chose to revert {0} to {1}".format(ioc, user_choice))
        except subprocess.CalledProcessError as cancel:
            if globals.verbose:
                print("User cancelled the operation")
                sys.exit(-1)
        
        if globals.verbose:
            print("Reverting {0} to {1}".format(ioc, user_choice))

        revert_arg = {}
        revert_arg['name'] = name
        revert_arg['type'] = type
        revert_arg['releaseFolder'] = toolconfig.determineReleaseFolder(facility, name, type)
        revert_arg['linkFolder'] = toolconfig.determineLinkFolder(facility, name, type)
        revert_arg['isMaster'] = False
        revert_arg['facility'] = facility
        revert_arg['ioc'] = ioc
        revert_arg['release'] = user_choice
        revert_arg['user'] = getpass.getuser()
        revert_response = multi_facility_process_helper.upgrade(revert_arg)
        if not revert_response:
            subprocess.call(['zenity', '--info', '--text=There was some issue when reverting ' + ioc + ' to ' + user_choice + '. Please see the console for more information'])
            sys.exit(-1)

        subprocess.call(['zenity', '--info', '--text=IOC ' + ioc + ' has been reverted to ' + user_choice + '. Please restart the IOC for these changes to take effect.'])

    else:
        if 'MASTER' not in versions:
            subprocess.call(['zenity', '--info', '--text=The app ' + name + ' does not have any history in this facility.'])
            sys.exit(-1)

        # The first item in the history is the current release. For usability, we remove this.
        package_versions = versions['MASTER'][1:]
        if globals.verbose:
            print("History of package {0} is {1}".format(name, package_versions))
        
        versions_for_zenity = []

        for version in package_versions:
            versions_for_zenity.append(version['release'])
            versions_for_zenity.append(version['datetime'])
            versions_for_zenity.append(version['user'])
            
        if not versions_for_zenity:
            subprocess.call(['zenity', '--info', '--text=The package' + name + ' does not have any history in this facility.'])
            sys.exit(-1)
            
        if globals.verbose:
            print(versions_for_zenity)
        # Pop up a zenity list dialog with the releases
        zenity_args = ['zenity', '--list',
                                 '--title', 'Choose the previous version of the package {0} that you want to revert to'.format(name),
                                 '--width=500', '--height=600',
                                 '--column=Version', '--column=Date', '--column=User']
        zenity_args.extend(versions_for_zenity)
        try:
            user_choice = subprocess.check_output(zenity_args).decode('utf-8').strip()
            if globals.verbose:
                print("User chose to revert {0} to {1}".format(name, user_choice))
        except subprocess.CalledProcessError as cancel:
            if globals.verbose:
                print("User cancelled the operation")
                sys.exit(-1)
        
        if globals.verbose:
            print("Reverting {0} to {1}".format(name, user_choice))

        revert_arg = {}
        revert_arg['name'] = name
        revert_arg['type'] = type
        revert_arg['releaseFolder'] = toolconfig.determineReleaseFolder(facility, name, type)
        revert_arg['linkFolder'] = toolconfig.determineLinkFolder(facility, name, type)
        revert_arg['isMaster'] = True
        revert_arg['facility'] = facility
        revert_arg['ioc'] = 'MASTER'
        revert_arg['release'] = user_choice
        revert_arg['user'] = getpass.getuser()
        revert_response = multi_facility_process_helper.upgrade(revert_arg)
        if not revert_response:
            subprocess.call(['zenity', '--info', '--text=There was some issue when reverting ' + name + ' to ' + user_choice + '. Please see the console for more information'])
            sys.exit(-1)

        subprocess.call(['zenity', '--info', '--text=The package ' + name + ' has been reverted to ' + user_choice + '. Please restart the application for these changes to take effect.'])


if __name__ == "__main__":
    execute(sys.argv[1:])
