/*
* Licensed Materials - Property of IBM* and/or HCL**
* UrbanCode Deploy
* UrbanCode Build
* UrbanCode Release
* AnthillPro
* (c) Copyright IBM Corporation 2011, 2017. All Rights Reserved.
* (c) Copyright HCL Technologies Ltd. 2018. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*
* * Trademark of International Business Machines
* ** Trademark of HCL Technologies Limited
*/
import com.ibm.urbancode.zos.common.DeploymentConstants;
import com.ibm.urbancode.zos.common.DeploymentHelper;
import com.ibm.urbancode.zos.common.PackageManifestXMLHelper
import com.ibm.team.enterprise.deployment.toolkit.manifest.GenerateRollbackManifest;
import com.ibm.urbancode.zos.deploy.common.DeploymentResultHelper;
import com.urbancode.air.AirPluginTool
import com.urbancode.ud.client.EnvironmentClient
import org.codehaus.jettison.json.JSONArray
import org.codehaus.jettison.json.JSONObject


try{
    final def workDir = new File('.').canonicalFile
    def apTool = new AirPluginTool(this.args[0], this.args[1])
    def props = apTool.getStepProperties();

    final def resourceId     = DeploymentHelper.getStringInput(props['resourceId']);
    final def environmentId  = DeploymentHelper.getStringInput(props['environmentId']);
    final def versionId      = DeploymentHelper.getStringInput(props['versionId']);
    final def componentId    = DeploymentHelper.getStringInput(props['componentId']);
    final def componentName     = DeploymentHelper.getStringInput(props['componentName']);
    final def versionName         = DeploymentHelper.getStringInput(props['versionName']);
    final def versionType    = DeploymentHelper.getStringInput(props['versionType']);
    final def deployBasePath     = DeploymentHelper.getStringInput(props['deployBasePath']);
    final def tempDsnPrefix      = DeploymentHelper.getStringInput(props['tempDsnPrefix']);
    final def preventRiskyRollback = DeploymentHelper.getBooleanInput(props['preventRiskyRollback']);
    final def dryRunForRisky = DeploymentHelper.getBooleanInput(props['dryRunForRisky']);
    final def toolKitHome    = DeploymentHelper.getStringInput(System.getenv().get("BUZ_TOOLKIT_HOME"));
   def oldVersionIdentifier=true;

    if (!toolKitHome) {//try to compatible with application build on old plug-in(before v5)
        toolKitHome = DeploymentHelper.getStringInput(props['ucdToolKitHome']);
    }

    if ("FULL".equalsIgnoreCase(versionType)) {
        DeploymentHelper.printProcessOutput("Error: Error: Rollback is not supported for full version ${versionName}");
        System.exit(1);
    }

    final def checkAccess        = DeploymentHelper.getBooleanInput(props['checkAccess']);
    final def zosScriptPath      = DeploymentHelper.getPath4ExecScriptInToolkit(toolKitHome , DeploymentConstants.ZOS_TOOLKIT_ISPF_SCRIPT_NAME);
    final def checkAccessExePath = DeploymentHelper.getPath4Checkaccess(toolKitHome , DeploymentConstants.CHECK_ACCESS_EXE);

    DeploymentHelper.validateDirectoryExist(deployBasePath, "Deploy Base Path ${deployBasePath} does not exist!");

    def pkgManifestHelper = new PackageManifestXMLHelper();

    final def deployBasePathForVersion  = DeploymentHelper.getVersionPathInDeployBasePath(deployBasePath, resourceId, componentName, versionName, true);
    final def rollbackMfFilePath        = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.ROLLBACK_MANIFEST_FILE_NAME);
    final def backupZipFilePath         = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.DEPLOY_BACKUP_FILE_NAME);
    final def containerFilePath         = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.CONTAINER_MAPPER_FILE_NAME);
	final def containerHFSFilePath         = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME);
    final def dsListFilePath            = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME);
    final def packageManifestFilePath   = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);

    if (!new File(deployBasePathForVersion).exists()) {
        println "Please make sure you have deployed the version successfully.";
        System.exit(1);
    }

    final def hasDataSets = pkgManifestHelper.hasDataSets(packageManifestFilePath);
    final def hasHFSFiles = pkgManifestHelper.hasHFSFiles(packageManifestFilePath);

    if (pkgManifestHelper.isGenericOnlyVersion(packageManifestFilePath)) {
        DeploymentHelper.printActionTitle("No artifacts to rollback. Version contains only generic artifacts.");
        System.exit(0);
    }
    
    if (!(hasDataSets || hasHFSFiles)) { // this is most likely when using delta deploy and nothing is deployed
        DeploymentHelper.printActionTitle("No artifacts to rollback. ");
        System.exit(0);
    }

    if (!DeploymentResultHelper.checkVersionDeployed(deployBasePath, resourceId, componentName, versionName)) {
        println "Please make sure you have deployed the version successfully and haven't rolled back it already.";
        System.exit(1);
    }

    def serverContainsCheckAPI = false;
    JSONArray overlappingArtifacts = null;
    try{
        DeploymentHelper.printActionTitle("Check risks for rollback.");
        def udUser = apTool.getAuthTokenUsername();
        def udPass = apTool.getAuthToken();
        def weburl = System.getenv("AH_WEB_URL");
        EnvironmentClient envClient = new EnvironmentClient(new URI(weburl), udUser, udPass);
        com.urbancode.air.XTrustProvider.install();
        overlappingArtifacts = envClient.getOverlappingArtifacts(environmentId, componentId, resourceId, versionId);
        serverContainsCheckAPI = true;
    }catch (IOException requestException) {
        if (requestException.message.contains("400 Bad Request")) {
            if (requestException.message.contains("Error processing command: null for uri:")) {
                DeploymentHelper.printProcessOutput("UCD Server 6.2.1.2 or later is required to do rick checking.");
                DeploymentHelper.printProcessOutput("Rick checking skipped.");
            }
            else if(requestException.message.contains("not deployed")) {
                DeploymentHelper.printActionTitle("Failed: " +versionName + " not deployed");
                apTool.setOutputProperty("reason", "1");
                apTool.setOutputProperty("reasonMessage", "Version not deployed.");
                System.exit(1);
            }
            else {
                throw requestException;
            }
        }
        else {
            throw requestException;
        }
    }catch (Exception e){
        DeploymentHelper.printProcessOutput("Warning: can not do risk checking.");
    }

    if (serverContainsCheckAPI) {
        if (overlappingArtifacts.length()>0) {
            DeploymentHelper.printProcessOutput("Risk checking failed for artifacts:");
            def jsonObjectLength = overlappingArtifacts.length();
            def jsonLoopIndex = 0;
            while (jsonLoopIndex < jsonObjectLength) {
                def riskyArtifact = overlappingArtifacts.getJSONObject(jsonLoopIndex);
                DeploymentHelper.printProcessOutput("  [" + riskyArtifact.getString("action") + "] "
                                                        + riskyArtifact.getString("name")
                                                        + " is replaced by subsequent version:"
                                                        + riskyArtifact.getString("versionName"));
                jsonLoopIndex++;
            }
        } else {
            DeploymentHelper.printProcessOutput("Risk checking passed.");
        }
    }

//    DeploymentHelper.printActionTitle("Risk checking completed.");

    if (serverContainsCheckAPI) {
        if (overlappingArtifacts.length()>0) {
            if (preventRiskyRollback) {
                DeploymentHelper.printProcessOutput("Rollback failed because risk is detected. A risky rollback tries to rollback modules which have been replaced by a subsequent version.");
                apTool.setOutputProperty("reason", "2");
                apTool.setOutputProperty("reasonMessage", "Risk found.");
                System.exit(1);
            }
            else {
                DeploymentHelper.printProcessOutput("Warning: continue with risky rollback because prevent risky rollback option is unchecked.");
            }
        }
    }

    if (dryRunForRisky) {
        DeploymentHelper.printProcessOutput("Dry run completed. Nothing is deployed.");
        System.exit(0);
    }

    //Validation
    def errorInfo = """ does not exist or is not a file.
      Error rolling back because one of the files needed for rollback is not found. 
      Please make sure you have deployed the version successfully and haven't rolled back it already."""
    DeploymentHelper.validateFileExist(rollbackMfFilePath, rollbackMfFilePath + errorInfo);
    final def wdPath = workDir.canonicalPath;
    
    final def currentVersionWorkingDir = DeploymentHelper.getVersionDirPathInWorkingDir(wdPath,versionName, resourceId);
    final def toDeployDeltaFilePath    = DeploymentHelper.getFilePathInWorkingDir(wdPath, versionName, resourceId, DeploymentConstants.DEPLOY_DELTA_FILE_NAME);
    final def toRollbackMfFilePath     = DeploymentHelper.getFilePathInWorkingDir(wdPath, versionName, resourceId, DeploymentConstants.ROLLBACK_MANIFEST_FILE_NAME);
    final def toBackupZipFilePath      = DeploymentHelper.getFilePathInWorkingDir(wdPath, versionName, resourceId, DeploymentConstants.DEPLOY_BACKUP_FILE_NAME);
    final def toContainerFilePath      = DeploymentHelper.getFilePathInWorkingDir(wdPath, versionName, resourceId, DeploymentConstants.CONTAINER_MAPPER_FILE_NAME);
    final def toDsListFilePath         = DeploymentHelper.getFilePathInWorkingDir(wdPath, versionName, resourceId, DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME);
	final def toContainerHFSFilePath   = DeploymentHelper.getFilePathInWorkingDir(wdPath, versionName, resourceId, DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME);

    if(hasDataSets){
        //to rollback datasets, need the container mapping and ISPF client gateway
        DeploymentHelper.validateFileExist(containerFilePath, containerFilePath + errorInfo);
        DeploymentHelper.validateFileExist(zosScriptPath, "Can not find the ispf gateway script: ${zosScriptPath}. Please make sure z/OS Deploy Toolkit is installed and z/OS Toolkit Home is set to the correct location.");

        if( checkAccess && !DeploymentHelper.fileExists(dsListFilePath) ){
            println "Warning:The target dataset list file ${dsListFilePath} is not found, access check is skipped. "
        }
    }

	if(hasHFSFiles){
        //to rollback HFS files, need the container HFS mapping 
		def file = new File(containerHFSFilePath);
        if( (file.exists() && file.isFile()) ){
          oldVersionIdentifier=false;  
        }
    }
    
    //Do some clean work before copying files
    DeploymentHelper.cleanWorkDir(currentVersionWorkingDir);

    //Copy files need by rollback
    def antCopy = new AntBuilder();
    try {
        DeploymentHelper.printActionTitle("Copy backup into working directory:");
        antCopy.copy(verbose: "true",file:"$rollbackMfFilePath", tofile:"$toDeployDeltaFilePath",overwrite: "true");
        //backup.zip may not exist if the last deploy is create
        antCopy.copy(verbose: "true",file:"$backupZipFilePath", tofile:"$toBackupZipFilePath",overwrite: "true", failonerror: "false");
		if(hasHFSFiles){
		     antCopy.copy(verbose: "true",file:"$containerHFSFilePath", tofile:"$toContainerHFSFilePath",overwrite: "true");
		}
        if(hasDataSets){
            antCopy.copy(verbose: "true",file:"$containerFilePath", tofile:"$toContainerFilePath",overwrite: "true");
            if( checkAccess && DeploymentHelper.fileExists(dsListFilePath)){
                antCopy.copy(verbose: "true",file:"$dsListFilePath", tofile:"$toDsListFilePath",overwrite: "true");
            }
        }

        println "";
    }
    catch (Exception e) {
        DeploymentHelper.printActionTitle("Error getting backup data.${e.message}");
        System.exit(1);
    }

    //generate rollback manifest based on the deltaDeploy xml
    new GenerateRollbackManifest(toDeployDeltaFilePath,toRollbackMfFilePath);

    if(hasDataSets && checkAccess && DeploymentHelper.fileExists(dsListFilePath)){
        DeploymentHelper.printActionTitle("Check access for Roll back action:");
        DeploymentHelper.callCheckAccess(checkAccessExePath, toDsListFilePath, "output");
        println "";
    }

    //Rollback
    DeploymentHelper.rollback(zosScriptPath, currentVersionWorkingDir, DeploymentConstants.ZOS_TOOLKIT_TRACE_ON, tempDsnPrefix,oldVersionIdentifier);

    final def delBackUpFiles = DeploymentHelper.getBooleanInput(props['delBackUpFiles']);
    if( delBackUpFiles ){
          DeploymentHelper.printActionTitle("Delete those backup files on request:") 
          DeploymentHelper.cleanBackUpData(deployBasePathForVersion);
        println "";
    }

    //Clear the working directory to avoid dirty data
    //Just delete those files we created
    DeploymentHelper.cleanWorkDir(currentVersionWorkingDir);
} catch (Exception e) {
    DeploymentHelper.printProcessOutput("${e.message}");
    System.exit(1);
}

System.exit(0);
