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

import com.ibm.urbancode.zos.common.SoftwareServiceInstance
import com.ibm.urbancode.zos.common.zOSMFInformation
import com.urbancode.air.AirPluginTool
import com.urbancode.ud.client.PropertyClient
import com.urbancode.ud.client.ResourceClient

def apTool = new AirPluginTool(this.args[0], this.args[1]);
def props = apTool.getStepProperties();

def weburl = System.getenv("AH_WEB_URL");
def user = apTool.getAuthTokenUsername();
def password = apTool.getAuthToken();
def rootResourcePath = props['resourcePath'];

def zOSMFURL = props['zOSMFURL'];
def zOSMFUser = props['user'];
def zOSMFPassword = props['password'];
def query = props['query']?.trim();

def WSDISC_HOME = System.getenv("PLUGIN_HOME");
def finalURI = new URI(weburl);
def udResourceClient = new ResourceClient(finalURI, user, password);
def udPropertyClient = new PropertyClient(finalURI, user, password);
def SoftwareServiceInstance softwareServiceInstance = new SoftwareServiceInstance(zOSMFURL,zOSMFUser,zOSMFPassword,true);
def zOSMFInformation = new zOSMFInformation(zOSMFURL,zOSMFUser,zOSMFPassword,true);
def lastInstanceResourcePath = ""; 
def allInstanceResourcePath = ""
zOSMFInformation.printRESTLOG = false;
softwareServiceInstance.printRESTLOG = false;

//this code parses the json that represents a resource and creates the resources
def createOrUpdateResources = { info, data ->
    String zOSMFServiceName = "zOSMFService"
    if(!rootResourcePath.endsWith(zOSMFServiceName)){
        println "Creating ${zOSMFServiceName} resource."
        try{
            udResourceClient.getResourceByPath(rootResourcePath+"/"+zOSMFServiceName);
        }catch(IOException e){            
            udResourceClient.createResource(zOSMFServiceName, null, null, rootResourcePath, null);
        }
        rootResourcePath = rootResourcePath + "/"+zOSMFServiceName;
    }
    
    def resArray = new JSONArray();
    def resMap = [:]; //to prevent duplicated resource
    
    def zOSMFServiceResource = new JSONObject();
    zOSMFServiceResource.put("path", "/${zOSMFServiceName}");
    zOSMFServiceResource.put("name", zOSMFServiceName);
    zOSMFServiceResource.put("roleName", "zOSMFService");

    def zOSMFServiceRoleProperties = new JSONObject();
    zOSMFServiceRoleProperties.put("zosmf.url", zOSMFURL)
    zOSMFServiceRoleProperties.put("zosmf.user", zOSMFUser)
    zOSMFServiceRoleProperties.put("zosmf.password", zOSMFPassword)
    zOSMFServiceRoleProperties.put("zosmf.query", query)
    zOSMFServiceRoleProperties.put("zosmf.zosmf_hostname", info.zosmf_hostname)
    zOSMFServiceRoleProperties.put("zosmf.zosmf_port", info.zosmf_port)
    zOSMFServiceRoleProperties.put("zosmf.zosmf_version", info.zosmf_version)
    zOSMFServiceRoleProperties.put("zosmf.zos_version", info.zos_version)
    zOSMFServiceRoleProperties.put("zosmf.api_version", info.api_version)
    zOSMFServiceRoleProperties.put("zosmf.zosmf_saf_realm", info.zosmf_saf_realm)
    zOSMFServiceRoleProperties.put("zosmf.zosmf_full_version", info.zosmf_full_version)
    zOSMFServiceResource.put("roleProperties", zOSMFServiceRoleProperties);
    resArray.put(zOSMFServiceResource);
    
    def count = 0;
    def pos = rootResourcePath.lastIndexOf("/");
    def parentPath = rootResourcePath.substring(0,pos);
    data.'scr-list'.each{
        println "Adding resource ${it.'external-name'}"
        def plexResource, systemResource, typeResource, instanceResource, instanceRoleProperties, instanceProperties;
        def path;

        //create plex folder
        path = "/${zOSMFServiceName}/PLEX ${it.sysplex}";
        if(resMap.containsKey(path)){
            plexResource = resMap[path];
        }else{
            println "Adding resource sysplex:${it.sysplex}"
            plexResource = new JSONObject();
            plexResource.put("name", "PLEX " + it.sysplex);
            plexResource.put("path", path);
            resArray.put(plexResource);
            resMap.put(path, plexResource)
        }
   
        //create system folder
        path = "/${zOSMFServiceName}/PLEX ${it.sysplex}/SYSTEM ${it.system}";
        if(resMap.containsKey(path)){
            systemResource = resMap[path];
        }else{
            println "Adding resource system:${it.system}"
            systemResource = new JSONObject();
            systemResource.put("name", "SYSTEM " + it.system);
            systemResource.put("path", path);
            resArray.put(systemResource);
            resMap.put(path, systemResource)
        }

        //create type folder
        path = "/${zOSMFServiceName}/PLEX ${it.sysplex}/SYSTEM ${it.system}/${it.type}";
        if(resMap.containsKey(path)){
            systemResource = resMap[path];
        }else{
            println "Adding resource type:${it.type}"
            typeResource = new JSONObject();
            typeResource.put("name", it.type);
            typeResource.put("path", path);
            resArray.put(typeResource);
            resMap.put(path, typeResource)
        }
       
        instanceResource =new JSONObject();
        path = "/${zOSMFServiceName}/PLEX ${it.sysplex}/SYSTEM ${it.system}/${it.type}/${it.'external-name'}"
        lastInstanceResourcePath = parentPath + path
        if(allInstanceResourcePath.length()>0){
            allInstanceResourcePath = allInstanceResourcePath + "," + lastInstanceResourcePath
        }else{
            allInstanceResourcePath = lastInstanceResourcePath
        }
        
        instanceResource.put("name", it.'external-name');
        instanceResource.put("path", path);
        instanceResource.put("roleName", "zOSMFSoftwareInstance");
        
        instanceRoleProperties = new JSONObject();
        instanceRoleProperties.put("instance.external-name", it.'external-name');
        instanceRoleProperties.put("instance.object-name", it.'object-name');
        instanceRoleProperties.put("instance.object-id", it.'object-id');
        instanceRoleProperties.put("instance.type", it.type);
        instanceRoleProperties.put("instance.state", it.state);
        instanceRoleProperties.put("instance.last-action-name", it.'last-action-name');
        instanceRoleProperties.put("instance.last-action-state", it.'last-action-state');
        instanceRoleProperties.put("instance.domain-name", it.'domain-name');
        instanceRoleProperties.put("instance.tenant-name", it.'tenant-name');
        instanceRoleProperties.put("instance.created-time", it.'created-time');
        instanceRoleProperties.put("instance.created-by-user", it.'created-by-user');
        instanceResource.put("roleProperties", instanceRoleProperties);
        
        resArray.put(instanceResource);
        count ++;
    }
    
    //create resources
    String reses = resArray.toString();
    // Send the list of resources to create to the resource restapi for batch create
    if (reses != null && !reses.trim().isEmpty()) {
        println "Batch creating resource."
        println ""
        udResourceClient.batchSaveResource(rootResourcePath, reses);
    }
    
    softwareServiceInstance.printRESTLOG = false;
    for (int i = 0 ; i < resArray.length(); i++) {
        JSONObject res = resArray.getJSONObject(i);
        def name = res.get("name");
        def path = res.get("path");
        if(res.has("roleName") && res.get("roleName") == "zOSMFSoftwareInstance"){
            println "Updating properties for resource ${name}."
            def objectId = res.get("roleProperties").get("instance.object-id");
            def objectVariables = softwareServiceInstance.getVariables(objectId);
            if(objectVariables && objectVariables["variables"]){
                def resourcePath = parentPath + path;
                setResourceProperties(objectVariables["variables"],resourcePath, udResourceClient, udPropertyClient);
            }else{
                println "Failed to get variables from software service instances $name, $objectId!";
            }
        }
    }
    println ""
    println "${count} instances discovered.";
    apTool.setOutputProperty("zosmf.service", zOSMFServiceName);
    apTool.setOutputProperty("zosmf.allInstanceResourcePath", allInstanceResourcePath);
    apTool.setOutputProperty("zosmf.lastInstanceResourcePath", lastInstanceResourcePath);
    apTool.setOutputProperty("zosmf.instanceCount", "" + count);
    apTool.storeOutputProperties();
}    

def getzOSMFInfo = {
    def data = zOSMFInformation.getInformation();
    if(!data){
        throw new RuntimeException("Failed to get zOSMF API information. Ending discovery process.");
    }else{
        return data;
    }
}

def getDataFromzOSMF = { q ->
    def data = softwareServiceInstance.listSoftwareServicesInstances(q);
    if(!data){
        throw new RuntimeException("Failed to list software service instances. Ending discovery process.");
    }else{
        return data;
    }
}

def setResourceProperties (properties, resourcePath, udResourceClient, udPropertyClient) {
    JSONObject propDefJson = new JSONObject();
    StringBuilder sb = new StringBuilder();
    properties.each() {
        sb.append(it["name"]);
        sb.append("=");
        sb.append(it["value"] ? it["value"] : "");
        sb.append("\n");
    }
    propDefJson.put("properties", sb.toString());

    JSONObject resourceInfo = udResourceClient.getResourceByPath(resourcePath);
    if (resourceInfo && resourceInfo.has("propSheet")) {
        JSONObject propSheet = resourceInfo.getJSONObject("propSheet");
        udPropertyClient.updateResourcePropValues(propSheet.getString("path"), propSheet.getString("version"), propDefJson);
    }
    else {
        println "Failed to find resource info for $resourcePath!";
        return false;
    }
    return true;
}
  
println "Retrieving zOSMF information."
def info = getzOSMFInfo();
println "Listing software services instances."
def data = getDataFromzOSMF(query);
createOrUpdateResources(info, data);

println "Discovery completed."



