#!/usr/bin/env groovy
/*
* 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 java.io.IOException
import java.net.URLEncoder
import java.nio.charset.Charset
import java.util.UUID

import com.urbancode.air.AirPluginTool
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder
import com.ibm.uclab.csrepl.client.ops.Download
import com.ibm.uclab.csrepl.client.ops.DownloadSync

import org.apache.http.HttpResponse
import org.apache.http.HttpStatus
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler
import org.apache.http.util.EntityUtils
import org.codehaus.jettison.json.JSONObject

def apTool = new AirPluginTool(args[0], args[1])

def PROXY_HOST = System.env['PROXY_HOST']
def PROXY_PORT = System.env['PROXY_PORT']
if (PROXY_PORT != null) {
    PROXY_PORT = Integer.valueOf(PROXY_PORT)
}

def workDir = new File('.').canonicalFile
def props = apTool.getStepProperties()

def MAX_TRIES = 3

def artifactSetBaseDir = props['artifactSetBaseDir']
def versionId = Util.parseUUID(props['versionId'])
def directoryOffset = props['directoryOffset']
def fileIncludePatterns = props['fileIncludePatterns']
def fileExcludePatterns = props['fileExcludePatterns']
def syncMode = props['syncMode']
def fullVerification = props['fullVerification'] == 'true'
def resId = props['resId']
def compId = props['compId']
def label = props['label']
def serverURL = props['serverUrl']
def setFileExecuteBits = (props['setFileExecuteBits'] != null && Boolean.valueOf(props['setFileExecuteBits']))
def verifyFileIntegrity = (props['verifyFileIntegrity'] != null && props['verifyFileIntegrity'] == "true")
def charset = Util.getCharset(props)

def baseDirectory = workDir
if (directoryOffset) {
    baseDirectory = new File(workDir, directoryOffset).canonicalFile
}

def includes = fileIncludePatterns?.readLines()
def excludes = fileExcludePatterns?.readLines()

def useSync = syncMode != 'false'
def fullClean = syncMode == 'FULL'
def syncCleanup = useSync
def stagedFiles = []
def currentDirs = []
def httpClient = null


def resolveVersionId = {
    def url = serverURL
    while (url.endsWith("/")) {
        url = url.substring(0, url.length - 1)
    }
    def component = URLEncoder.encode(compId).replace("+", "%20")
    def version = URLEncoder.encode(label).replace("+", "%20")
    def reqUrl = "${url}/cli/version/getVersionId?component=${component}&version=${version}"

    def builder = new HttpClientBuilder()
    if (PROXY_HOST) {
        builder.setProxyHost(PROXY_HOST)
        builder.setProxyPort(PROXY_PORT)
    }
    builder.setUsername(apTool.getAuthTokenUsername())
    builder.setPassword(apTool.getAuthToken())
    builder.setPreemptiveAuthentication(true)
    builder.setTrustAllCerts(true)
    def client = builder.buildClient()
    
    def code = 0
    def tries = 0
    def content = ""
    for (;;) {
        try {
            def req = new HttpGet(reqUrl)
            def res = client.execute(req)
            code = res.getStatusLine().getStatusCode()
            content = EntityUtils.toString(res.getEntity())
            break
        }
        catch (Exception e) {
            tries++
            if (tries >= MAX_TRIES) {
                throw e;
            }
            println "Error resolving version ID : " + e.getMessage()
        }
    }
    
    switch (code) {
    case 200:
        return UUID.fromString(content.trim())
    default:
        println "Could not resolve version ID. Method returned code ${code}"
        return null
    }
}

def getPreviousVersionId = {
    def url = serverURL
    while (url.endsWith("/")) {
        url = url.substring(0, url.length - 1)
    }
    def reqUrl = "${url}/rest/inventory/versionByResourceAndComponent/${resId}/${compId}"
    
    def builder = new HttpClientBuilder()
    if (PROXY_HOST) {
        builder.setProxyHost(PROXY_HOST)
        builder.setProxyPort(PROXY_PORT)
    }
    builder.setUsername(apTool.getAuthTokenUsername())
    builder.setPassword(apTool.getAuthToken())
    builder.setPreemptiveAuthentication(true)

    String verifyServerIdentityString = System.getenv().get("UC_TLS_VERIFY_CERTS");
    Boolean trustAllCerts = Boolean.valueOf(verifyServerIdentityString);
    builder.setTrustAllCerts(!trustAllCerts);

    def client = builder.buildClient()
    
    def code = 0
    def tries = 0
    def content = ""
    for (;;) {
        try {
            def req = new HttpGet(reqUrl)
            def res = client.execute(req)
            code = res.getStatusLine().getStatusCode()
            content = EntityUtils.toString(res.getEntity())
            break
        }
        catch (Exception e) {
            tries++
            if (tries >= MAX_TRIES) {
                throw e;
            }
            println "Error getting label for resource : " + e.getMessage()
        }
    }
    
    switch (code) {
    case 200:
        def res = new JSONObject(content)
        if (!res.isNull("version")) {
            JSONObject version = res.getJSONObject("version")
            if (!version.isNull("id")) {
                return UUID.fromString(version.getString("id"))
            }
        }
        return null
    case 404:
        println "Server returned 404 for getting the previous label"
        return null
    default:
        println "Could not get previous label. Method returned code ${code}"
        return null
    }
}

//
// Validation
//
baseDirectory.mkdirs()
if (baseDirectory.isFile()) {
    throw new IllegalArgumentException("Base directory ${baseDirectory} is a file!")
}

Util.configureLogging()

//
// Download the files
//

if (!versionId) {
    println "Cannot parse version ID - attempting to resolve from component ID and label..."
    println "  Component ID: ${compId}"
    println "  Label: ${label}"
    def resolvedVersionId = resolveVersionId()
    if (resolvedVersionId) {
        println "Resolved version ID ${resolvedVersionId}"
        versionId = resolvedVersionId
    }
    else {
        println "Resolution failed - terminating step."
        System.exit(1)
    }
}

def prevVersionId = null
if (useSync) {
    prevVersionId = getPreviousVersionId()
}

def forceUseServerUrl = false
def csClient = Util.getCodestationClient(
    serverURL, 
    forceUseServerUrl, 
    apTool.getAuthTokenUsername(), 
    apTool.getAuthToken())

def op
if (useSync) {
    op = new DownloadSync(csClient, baseDirectory, versionId, prevVersionId)
    op.setFullClean(fullClean)
}
else {
    op = new Download(csClient, baseDirectory, versionId)
}

op.setIncludes(includes)
op.setExcludes(excludes)
op.setOutputCharset(charset)
op.setSetExecuteBits(setFileExecuteBits)
op.setSourcePathOffset(artifactSetBaseDir)
op.setVerifyFiles(verifyFileIntegrity)
op.setSlowVerification(fullVerification)

println "Artifact source: ${csClient.url}"
println "Working directory: ${baseDirectory.path}"
println "Including ${fileIncludePatterns}"
println "Excluding ${fileExcludePatterns}"
println "Offset ${directoryOffset}"
println "Artifact base directory ${artifactSetBaseDir}"
println "Set file bits are ${setFileExecuteBits}"
println ""

op.run()
