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

/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* IBM UrbanCode Deploy
* IBM UrbanCode Release
* IBM AnthillPro
* (c) Copyright IBM Corporation 2002, 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/

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 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"));
	final def hfsTargetDir     = DeploymentHelper.getStringInput(props['hfsTargetDir']);
	
    if (!toolKitHome) {//try to compatible with application build on old plug-in(before v5)
        toolKitHome = DeploymentHelper.getStringInput(props['ucdToolKitHome']);
    }

    final def checkAccess        = DeploymentHelper.getBooleanInput(props['checkAccess']);
    final def zosScriptPath      = DeploymentHelper.getPath4ExecScriptInToolkit(toolKitHome , DeploymentConstants.ZOS_TOOLKIT_ISPF_SCRIPT_NAME);
    final def checkAccessExePath = DeploymentHelper.getPath4ExecScriptInToolkit(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 dsListFilePath            = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME);
    final def packageManifestFilePath   = DeploymentHelper.getFilePathInDeployBasePath(deployBasePathForVersion, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);

	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 (!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);
    }
	
	//verify input parameters for HFS file deployment
	if(hasHFSFiles){
		if (null == hfsTargetDir || hfsTargetDir.length()<=0) {
			throw new IllegalArgumentException("Version contains HFS files. HFS target directory is required. ");
		}
		if(!(new File(hfsTargetDir).exists())){
			throw new IllegalArgumentException("Rollback failed because HFS target directory(${hfsTargetDir}) does not exist");
		}else if (!(new File(hfsTargetDir).isDirectory())){
			throw new IllegalArgumentException("Rollback failed because HFS target directory(${hfsTargetDir}) is not a directory.");
		}
	}

	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);

	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. "
	    }
	}
	
	
	//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");
		//container mapping is not available for HFS only versions
		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, hfsTargetDir);

    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);
