/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Deploy
* (c) Copyright IBM Corporation 2011, 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
package com.urbancode.air.plugin.udeploy.environments

import com.urbancode.air.AirPluginTool
import com.urbancode.ud.client.EnvironmentClient
import java.util.UUID
import java.net.URI
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;


public class EnvironmentHelper {
    def apTool
    def props = []
    def udUser
    def udPass
    def weburl
    EnvironmentClient client

    public EnvironmentHelper(def apToolIn) {
        apTool = apToolIn
        props = apTool.getStepProperties()
        udUser = apTool.getAuthTokenUsername()
        udPass = apTool.getAuthToken()
        weburl = System.getenv("AH_WEB_URL")
        client = new EnvironmentClient(new URI(weburl), udUser, udPass)

        com.urbancode.air.XTrustProvider.install()
    }

    public def createEnvironment() {
        def appName = props['application']
        def envName = props['environment']
        def description = props['description']
        def requireApprovals = Boolean.valueOf(props['requireApprovals'])
        def appendSuffix = Boolean.valueOf(props['suffix'])
        def propertiesString = props['properties']
        def nodePropertiesString = props['nodeProperties']
        def nodePropertiesJSON = null

        def blueprintName = props['blueprint']
        def baseResource = props['baseResource']
        def envProfileName = props['envProfileName']

        if (!appName) {
            throw new IllegalArgumentException("No application was specified")
        }
        if (!envName) {
            throw new IllegalArgumentException("No environment was specified")
        }
        if (!description) {
            description = ""
        }

        if (blueprintName && !baseResource) {
            throw new IllegalArgumentException("A base resource path must be specified when using "+
                    "a blueprint to create an environment.");
        }

        if (appendSuffix) {
            envName = envName + "-" + UUID.randomUUID().toString().substring(0,8)
        }

        if (nodePropertiesString) {
            nodePropertiesJSON = new JSONObject();
            String[] splitNewlines = nodePropertiesString.split("\n");
            def properties = new Properties()
            properties.load(new ByteArrayInputStream(nodePropertiesString.getBytes()))

            for (String line : splitNewlines) {
                String[] splitEquals = line.split("=", 2);
                String key = splitEquals[0];
                String value = splitEquals[1];
                int lastSlash = key.lastIndexOf('/')
                String node = key.substring(0, lastSlash)
                String name = key.substring(lastSlash+1)
                JSONObject nodeJSON = nodePropertiesJSON.optJSONObject(node) ?: new JSONObject();
                nodeJSON.put(key, value)
                nodePropertiesJSON.put(node, nodeJSON)
            }
        }

        UUID envUUID = client.createEnvironment(appName, envName, description, "white",
        requireApprovals, blueprintName, baseResource, envProfileName, nodePropertiesJSON)
        println "Created environment with ID " + envUUID.toString() + "\n"

        if (propertiesString) {
            def properties = new Properties()
            properties.load(new ByteArrayInputStream(propertiesString.getBytes()))

            for (def key : properties.keySet()) {
                def value = properties.get(key)

                client.setEnvironmentProperty(envName, appName, key, value, false)
                println "Environment property " + key + " was set for environment " + envName
            }
        }

        apTool.setOutputProperty("environment.name", envName)
        apTool.setOutputProperty("environment.id", envUUID.toString())
        apTool.setOutputProperties()
    }

    public def deleteEnvironment() {
        def appName = props['application']
        def envName = props['environment']

        client.deleteEnvironment(appName, envName)
        println "Deleted Environment "+envName;
    }

    public def setEnvironmentProperty() {
        def appName = props['application']
        def envName = props['environment']
        def propName = props['name']
        def propValue = props['value']
        def isSecure = Boolean.valueOf(props['isSecure'])

        if (!propName) {
            throw new IllegalArgumentException("no property name was specified")
        }
        if (!propValue) {
            propValue = ""
        }
        if (!appName) {
            appName = ""
        }
        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        client.setEnvironmentProperty(envName, appName, propName, propValue, isSecure)
        println "Environment property " + propName + " was set for environment " + envName
    }

    def setComponentEnvironmentProperty() {
        def compName = props['component']
        def appName = props['application']
        def envName = props['environment']
        def propName = props['name']
        def propValue = props['value']
        def isSecure = Boolean.valueOf(props['isSecure'])

        if (!propName) {
            throw new IllegalArgumentException("no property name was specified")
        }
        if (!propValue) {
            propValue = ""
        }
        if (!appName) {
            appName = ""
        }
        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        if (!compName) {
            throw new IllegalArgumentException("no component was specified")
        }
        client.setComponentEnvironmentProperty(compName, envName, appName, propName, propValue, isSecure)
        println "component environment property " + propName + " was set for environment " + envName
    }

    def getEnvironmentProperties() {
        def appName = props['application']
        def envName = props['environment']

        if (!appName) {
            appName = ""
        }
        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }

        Map<String, String> propMap = client.getEnvironmentProperties(envName, appName)
        Set<String> keySet = propMap.keySet()
        for(String key : keySet) {
            apTool.setOutputProperty(key, propMap.get(key))
        }
        apTool.setOutputProperties()
    }

    def getComponentEnvironmentProperties() {
        def appName = props['application']
        def envName = props['environment']
        def compName = props['component']

        if (!appName) {
            appName = ""
        }
        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        if (!compName) {
            throw new IllegalArgumentException("no component was specified")
        }

        Map<String, String> propMap = client.getComponentEnvironmentProperties(compName, envName, appName)
        Set<String> keySet = propMap.keySet()
        for(String key : keySet) {
            apTool.setOutputProperty(key, propMap.get(key))
        }
        apTool.setOutputProperties()
    }

    def verifyInventoryStatus() {
        def envName = props['environment']
        def appName = props['application']
        def component = props['component']
        def version = props['version']
        def inventoryStatus = props['inventoryStatus']

        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        if (!appName) {
            appName = ""
        }
        if (!component) {
            throw new IllegalArgumentException("no component was specified")
        }
        if (!version) {
            throw new IllegalArgumentException("no version was specified")
        }
        if (!inventoryStatus) {
            throw new IllegalArgumentException("no status was specified")
        }

        boolean verified = client.verifyInventoryStatus(envName, appName, component, version, inventoryStatus)
        if (verified) {
            println "Confirmed that environment ${envName} has component ${component} in inventory with version ${version} and status ${inventoryStatus}"
            apTool.setOutputProperty("verified", "true")
        }
        else {
            println "Environment ${envName} does NOT have component ${component} in inventory with version ${version} and status ${inventoryStatus}"
            apTool.setOutputProperty("verified", "false")
        }
        apTool.setOutputProperties()
    }

    def environmentExists() {
        def envName = props['environment']
        def appName = props['application']

        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        if (!appName) {
            appName = ""
        }

        try {
            def envID = client.getEnvironmentUUID(envName, appName)
            println "Environment with name ${envName} was found."
            apTool.setOutputProperty("exists", "true");
        }
        catch(IOException e) {
            if(e.getMessage().contains("404") || e.getMessage().contains("400")) {
                println "Request was successful but no environment with name ${envName} was found."
                apTool.setOutputProperty("exists", "false");
            }
            else {
                println "An error occurred during your request."
                throw new IOException(e);
            }
        }
        apTool.setOutputProperties()
    }

    def getLatestEnvironmentInventoryByComponent() {
        def envName  = props['environment']
        def appName  = props['application']
        def compName = props['component']

        if (!envName) {
            throw new IllegalArgumentException("No environment was specified")
        }
        if (!appName) {
            throw new IllegalArgumentException("No application was specified")
        }
        if (!compName) {
            throw new IllegalArgumentException("No component was specified")
        }
        JSONObject inventory = client.getLatestEnvironmentInventoryByComponent(
            envName, appName, compName);

        if (inventory.length() == 0) {
            println "No version for that component exists for that environment"
        }
        else {
            def verName = inventory.get("version").getAt("name")
            def verId = inventory.get("version").getAt("id")
            apTool.setOutputProperty("version.name", verName)
            apTool.setOutputProperty("version.id", verId)
            apTool.setOutputProperties()
        }
    }

    public def addEnvironmentToTeam() {
        def appName = props['application']
        def envName = props['environment']
        def teamName = props['team']
        def typeName = props['type']

        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        if (!teamName) {
            throw new IllegalArgumentException("no team was specified")
        }

        client.addEnvironmentToTeam(appName, envName, teamName, typeName)
        println "Environment was added to team for the given type classification."
    }

    public def addBaseResource() {
        def appName = props['application']
        def envName = props['environment']
        def resName = props['resource']

        if (!envName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        if (!resName) {
            throw new IllegalArgumentException("no resource was specified")
        }

        client.addEnvironmentBaseResource(appName, envName, resName)
        println "The base resource was added to the environment."
    }

    public def getEnvironmentInfo() {
        def environmentName = props['environment']
        if (!environmentName) {
            throw new IllegalArgumentException("no environment was specified")
        }
        def applicationName = props['application']

        def environmentJson = client.getEnvironment(environmentName, applicationName)
        def environmentInfoMap = client.getJSONAsProperties(environmentJson)
        for (String key : environmentInfoMap.keySet()) {
            apTool.setOutputProperty(key, environmentInfoMap.get(key))
        }
        apTool.setOutputProperties()

        println environmentJson
    }
}
