/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* IBM UrbanCode Deploy
* IBM UrbanCode Release
* IBM AnthillPro
* (c) Copyright IBM Corporation 2002, 2016. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
import com.urbancode.air.AirPluginTool
import com.urbancode.air.CommandHelper
import org.yaml.snakeyaml.Yaml
import java.net.URI
import java.io.File
import com.urbancode.ud.client.ResourceClient
import com.urbancode.ud.client.ComponentClient
import com.urbancode.ud.client.ApplicationClient
import com.urbancode.ud.client.VersionClient
import com.urbancode.ud.client.EnvironmentClient
import org.codehaus.jettison.json.JSONArray
import org.codehaus.jettison.json.JSONException
import org.codehaus.jettison.json.JSONObject
import com.urbancode.air.XTrustProvider
import groovy.json.JsonSlurper
import com.urbancode.air.plugin.docker.DockerUtils

XTrustProvider.install()
int MAX_RETRIES = 3
def COMPONENT_TAG="compose.service"
def airTool = new AirPluginTool(this.args[0], this.args[1])
def props = airTool.getStepProperties()

def unJettison = { json ->
    def result = null
    def text = json.toString()

    if (text) {
        result = new JsonSlurper().parseText(text)
    }
    return result
}

def serverUri = new URI(props["serverUrl"])
String username = airTool.getAuthTokenUsername()
String password = props["password"] ?: airTool.getAuthToken()
String resourceId = props["resource"]
String applicationId = props["application"]
String componentProcessRequestId = props["componentProcessRequest"]
String composeProjectName = props["composeProjectName"]
String composeOptions = props["composeOptions"]
String environmentId = props["environment"]
String propertyPrefix = props["propertyPrefix"]
String scriptPathsRaw = props["scriptPaths"]

def scriptPaths = scriptPathsRaw.split(",")
        .findAll{ it?.trim() }
        .collect{ it.trim() }

String composeFilesRaw = props["composeFiles"]
def composeFilePaths = composeFilesRaw.split("\n")
        .findAll{ it?.trim() }
        .collect{ it.trim() }

if (!composeFilePaths) {
    composeFilePaths << "docker-compose.yml"
}

def versionOverridesFile = new File("ucd-version-overrides.yml")
if (versionOverridesFile.exists() && versionOverridesFile.text) {
    composeFilePaths << versionOverridesFile.path
}

ComponentClient componentClient = new ComponentClient(serverUri, username, password)
ApplicationClient applicationClient = new ApplicationClient(serverUri, username, password)
EnvironmentClient environmentClient = new EnvironmentClient(serverUri, username, password)

String deploymentRequestId = componentClient.getComponentProcessRequest(componentProcessRequestId)?.deploymentRequestId
def components = unJettison(applicationClient.getApplicationComponents(applicationId))

def desiredVersionMap = [:]
def services = [:]
def componentMap = [:]

components.each { component ->
    componentMap.put(component.name, component)
    def desiredInventoryEntry = unJettison(environmentClient.getLatestEnvironmentInventoryByComponent(environmentId, applicationId, component.name))
    if (desiredInventoryEntry) {
        desiredVersionMap.put(component.name, desiredInventoryEntry.version.name)
    }

    if (component.tags.find{ COMPONENT_TAG.equals(it.name) }) {
        def componentProps = componentClient.getComponentProperties(component.name)
        component.image = componentProps["docker.image.name"]

        def version = desiredVersionMap.get(component.name) //Needs to be replaced with property for service name
        String imageTag = "$component.image:$version"
        services.put(component.name, ["image": imageTag])
    }
}


def workDir = new File(".")
def ch = new CommandHelper(workDir)

def composeUpArgs = ['docker-compose']
if (composeOptions) {
    composeUpArgs << composeOptions
}
composeFilePaths.each { path ->
    composeUpArgs << "-f"
    composeUpArgs << path
}
composeUpArgs << "up"
composeUpArgs << "-d"

def tempScript = File.createTempFile("ucd-compose", ".sh")
tempScript.deleteOnExit()
tempScript.setExecutable(true)
String envPropValues = props["envPropValues"]

def envVars = [:]
if(envPropValues) {
   if (propertyPrefix) {
        println "Looking for properties starting with $propertyPrefix"
    }
    //this is jeffs magic regex to split on ,'s preceded by even # of \ including 0
    envPropValues.split("(?<=(^|[^\\\\])(\\\\{2}){0,8}),").each { prop ->
        //split out the name
        def parts = prop.split("(?<=(^|[^\\\\])(\\\\{2}){0,8})=",2);
        def propName = parts[0];
        def propValue = parts.size() == 2 ? parts[1] : "";
        //replace \, with just , and then \\ with \
        propName = propName.replace("\\=", "=").replace("\\,", ",").replace("\\\\", "\\")
        propValue = propValue.replace("\\=", "=").replace("\\,", ",").replace("\\\\", "\\")

        if ((!propertyPrefix || propName.startsWith(propertyPrefix))) {
            envVars.put(propName, propValue)
        }
    }
}

scriptPaths.each { filePath ->
    def envScript = new File(filePath)
    if (envScript.exists()) {
        envScript.setExecutable(true)
        tempScript << ". $filePath\n"
    }
    else {
        throw new RuntimeException("Could not find env file at $filePath")
    }
}

tempScript << composeUpArgs.join(" ")
ch.runCommand("Running the following commands:\n$tempScript.text", [tempScript.canonicalPath])

ResourceClient resourceClient = new ResourceClient(serverUri,  username, password)
def children = unJettison(resourceClient.getResourceChildren(resourceId))

//Update Actual (Resource) Inventory
children.each { childResource ->
    def roles = unJettison(resourceClient.getResourceRoles(childResource.id))
    roles.each { role ->
        //if child has role, update inventory
        def componentName = role.name
        def versions = unJettison(componentClient.getComponentVersionsJsonArray(componentName, false))
        def versionId
        def resourceInventoryEntry = services.get(componentName)
        if (resourceInventoryEntry) {
            def versionName = desiredVersionMap.get(componentName)
            println "Updating inventory for $componentName - $versionName"
            versions.each {
            if (versionName.equals(it.name)) {
                println "\t$it.name"
                versionId = it.id
                }
            }
            resourceClient.createResourceInventoryEntry(deploymentRequestId, childResource.id, componentMap.get(componentName).id, versionId, "Active")
        }
    }
}
