/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Deploy
 * (c) Copyright IBM Corporation 2014, 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.commons.httpcomponentsutil.HttpClientBuilder
import com.urbancode.commons.util.IO
import com.urbancode.ud.client.ComponentClient
import com.urbancode.ud.client.VersionClient
import groovy.json.JsonSlurper
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.util.EntityUtils

import java.nio.charset.Charset

def tempDirs = new HashMap<String, File>();

final def workDir = System.getProperty('java.io.tmpdir')
final def props = new Properties();
final def inputPropsFile = new File(args[0]);
final def inputPropsStream = null;
try {
    inputPropsStream = new FileInputStream(inputPropsFile);
    props.load(inputPropsStream);
}
catch (IOException e) {
    throw new RuntimeException(e);
}

final def agentProps = new Properties();
agentProps.load(new FileInputStream(new File(System.getenv("AGENT_HOME"),
        "conf/agent/installed.properties")));
String charsetName = agentProps.getProperty("system.default.encoding");
Charset charset = null;
if (charsetName != null) {
    charset = Charset.forName(charsetName);
}

final def REPO_PATH_SEPARATOR = "/";
def exitVal = 0;

boolean isUseVFS = Boolean.valueOf(props['isUseVFS'])
def componentName = props['componentName']
def username = props['user']
def password = props['password']
def groupId = props['groupId']
def artifactId = props['artifactId']
def extension = props['extension']
def copyCount = props['copyCount']
def versionPattern = props['versionPattern']
def specificVersionPattern = props['version']
def saveFileExecuteBits = props['saveFileExecuteBits']
def separator = props['separator'].trim()

def extensionsString = props['extensions']
String[] extensions = new String[0];
if (! isEmpty(extensionsString)) {
    extensions = extensionsString.split(",");
    for (int i = 0; i < extensions.length; i++) {
        extensions[i] = extensions[i].trim();
    }
}

def repoName = props['repoName']
String repoUrl = props['repoUrl']
while (repoUrl.endsWith(REPO_PATH_SEPARATOR)) {
    repoUrl = repoUrl.substring(0, repoUrl.length() - 1)
}

boolean isNuget = Boolean.valueOf(!isEmpty(props['nuGetPackage']) && !isEmpty(props['nuGetVersion']));

String UDUsername = "PasswordIsAuthToken";
String UDPassword = String.format("{\"token\": \"%s\"}", System.getenv("AUTH_TOKEN"));

String webUrl = System.getenv("AH_WEB_URL");
URI url = new URI(webUrl);

VersionClient versionClient = new VersionClient(url, UDUsername, UDPassword);
ComponentClient componentClient = new ComponentClient(url, UDUsername, UDPassword);

HttpClientBuilder builder = new HttpClientBuilder();
if (username) {
    builder.setPreemptiveAuthentication(true);
    builder.setUsername(username);
    builder.setPassword(password);
}
DefaultHttpClient client = builder.buildClient();

def getTempDir = { version ->
    File result = tempDirs.get(version);
    if (result == null) {
        result = new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
        IO.mkdirs(result);
        tempDirs.put(version, result);
    }

    return result;
}

def isEmpty(value) {
    return value == null || value.equals("")
}

def filterVersions = { versionList ->
    def result = []
    def versionsMatched = Integer.valueOf(copyCount);
    for (version in versionList) {
        // If we still need versions (Cannot break in Groovy)
        if (versionsMatched > 0) {
            boolean shouldAdd = false;
            // Make sure the overall component source configuration pattern is matched
            def versionPatternMatcher = version =~ versionPattern
            if (versionPatternMatcher.matches()) {

                // If they specified a specific version on the import dialog, only get that one
                if (specificVersionPattern != null && !specificVersionPattern.isEmpty()) {
                    def specificVersionMatcher = version =~ specificVersionPattern
                    if (specificVersionMatcher.matches()) {
                        shouldAdd = true
                    }
                } else {
                    // They want all the versions that match the source config pattern
                    shouldAdd = true
                }
            }

            if (shouldAdd) {
                result.add(version)
                versionsMatched--
            }
        }
    }

    return result
}

def getVersionList = {
    String versionUrl = (repoUrl + REPO_PATH_SEPARATOR + "api/storage/" + repoName + REPO_PATH_SEPARATOR
            + groupId + REPO_PATH_SEPARATOR + artifactId).toString();

    HttpGet get = new HttpGet(versionUrl);
    HttpResponse response = client.execute(get);
    int status = response.getStatusLine().getStatusCode();
    if (status == HttpStatus.SC_OK) {
        def versions = []
        def jsonString = EntityUtils.toString(response.getEntity());
        def slurper = new JsonSlurper();
        def children = slurper.parseText(jsonString).children;
        for (child in children) {
            def isFolder = child.folder.toString();
            if (isFolder.equals("true")) {
                def currVersion = child.uri.toString();
                versions.add(0, currVersion.substring(1, currVersion.length()));
            }
        }
        return versions;
    }
    else {
        throw new Exception("Exception searching: " + versionUrl + "\nErrorCode : " + status.toString());
    }
}

def getLatestVersions = {
    def versionList = getVersionList();

    if (isEmpty(versionPattern)) {
        versionPattern = ".*";
    }
    return filterVersions(versionList);
}

def downloadFile = { fileUrl, version ->
    fileUrl = fileUrl.replace("api/storage/", "");
    println "Downloading file: " + fileUrl
    HttpGet get = new HttpGet(fileUrl);
    HttpResponse response = client.execute(get);
    int status = response.getStatusLine().getStatusCode();
    if (status == HttpStatus.SC_OK) {
        String[] currFile = fileUrl.split(REPO_PATH_SEPARATOR);
        File file = new File(getTempDir(version), currFile[currFile.length - 1]);
        IO.copy(response.getEntity().getContent(), file);
        return getTempDir(version); //NuGet needs this return statement
    }
    else {
        throw new Exception("Exception downloading file : " + fileUrl + "\nErrorCode : " + status.toString());
    }
}

def download = { version ->
    String downloadUrl = (repoUrl + REPO_PATH_SEPARATOR + "api/storage/" + repoName + REPO_PATH_SEPARATOR
            + groupId + REPO_PATH_SEPARATOR + artifactId + REPO_PATH_SEPARATOR + version).toString();
    println "Downloading version: " + downloadUrl

    HttpGet get = new HttpGet(downloadUrl);
    HttpResponse response = client.execute(get);
    int status = response.getStatusLine().getStatusCode();
    if (status == HttpStatus.SC_OK) {
        def files = []
        def jsonString = EntityUtils.toString(response.getEntity());
        def slurper = new JsonSlurper();
        def children = slurper.parseText(jsonString).children;
        for (child in children) {
            def isFolder = child.folder.toString();
            if (isFolder.equals("false")) {
                def currChild = child.uri.toString();
                if (currChild =~ extension + "\$") {
                    downloadFile(downloadUrl + currChild, version)
                }
            }
        }

        return getTempDir(version);
    }
}

def integrate = { version ->
    UUID result = null;
    File tempDir = null;
    String[] includes = ["**/*"] as String[];
    String[] excludes = [] as String[];
    try {
        System.out.println(); // Put a new line in the output to separate versions

        boolean preserveExecutePermissions = Boolean.valueOf(props['saveFileExecuteBits']);
        List<String> versions = componentClient.getComponentVersions(componentName.toString(), false);
        boolean hasVersion = versions.contains(version);
        println ("Version from artifactory: " + version);
        if (!hasVersion) {
            println ("Creating new version");
            String versionId = versionClient.createVersion(componentName.toString(), version.toString(), " ").toString();
            if (isUseVFS) {
                if(!separator) {
                    separator = "."
                }
                def nugetPackage = props['nuGetPackage'] + separator + version + ".nupkg"
                println nugetPackage
                String nupkgUrl = (repoUrl + REPO_PATH_SEPARATOR + repoName + REPO_PATH_SEPARATOR + nugetPackage).toString();
                tempDir = isNuget ? downloadFile(nupkgUrl, version) : download(version);
                versionClient.addVersionFiles(componentName.toString(), versionId, tempDir, "", includes, excludes, preserveExecutePermissions, true, charset, extensions);
            } else {
                println (String.format("Not uploading version %s to VFS because using VFS was not selected.",
                        versionId));
            }
        } else {
            println (String.format("UCD already contains version %s. A duplicate will not be created.",
                    version));
        }
    } catch (Exception e) {
        System.err.println(String.format("Error creating a new version: %s", e.toString()));
        result = null;
    } finally {
        try {
            if (tempDir != null && tempDir.exists()) {
                IO.delete(tempDir);
            }
        } catch (IOException e) {
            System.err.println(String.format("Unable to delete download directory",
                    e.getMessage()));
        }
    }
    return result;
}

try {
    if (! isNuget) {
        def latestVersions = getLatestVersions()
        for (version in latestVersions) {
            integrate(version);
        }
    } else {
        integrate(props['nuGetVersion'].toString());
    }
    System.out.println(); // Put a new line in the output to separate the end of the imports
    System.out.println("Finished");
}
catch (Exception e) {
    e.printStackTrace();
    exitVal=1;
}
System.exit(exitVal);
