/*
* Licensed Materials - Property of IBM* and/or HCL**
* UrbanCode Deploy
* (c) Copyright IBM Corporation 2016, 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.nio.charset.Charset

import com.urbancode.air.AirPluginTool
import com.urbancode.air.XTrustProvider
import com.urbancode.air.plugin.odm.OdmRestClient
import com.urbancode.commons.util.IO
import com.urbancode.ud.client.ComponentClient
import com.urbancode.ud.client.VersionClient

Map tempDirs = new HashMap<String, File>()

final def airTool = new AirPluginTool(args[0], args[1])
final Properties props = airTool.getStepProperties()

String jarPath = props['jarPath']

// import teamserver .jar files
private void importOdmJars(String jarPath) {
    final String PLUGIN_HOME = System.getenv("PLUGIN_HOME")

    if (jarPath) {
        while (jarPath.endsWith(File.separator)) {
            jarPath = jarPath.substring(0, jarPath.length() - 1)
        }
        println("[Info] Loading jrules jars into classpath from path: " + jarPath)
        loadAllJars(jarPath)
    }
    else {
        File jrulesengine = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-engine.jar')
        File jruleslanguage = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-language.jar')
        File jrulesartifacts = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-ruleartifacts.jar')
        File jrulesteamserver = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-teamserver.jar')
        File jrulesrs4j = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-dataaccess-rs4j.jar')
        File jrulesdataaccess = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-model-dataaccess.jar')
        File jrulessynchronization = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'jrules-synchronization.jar')
        File wbemodel = new File(PLUGIN_HOME + File.separator + 'lib' + File.separator + 'wbemodel.jar')
        if (!jrulesengine.exists() || !jruleslanguage.exists() || !jrulesartifacts.exists() || !jrulesteamserver.exists() ||
            !jrulesrs4j.exists() || !jrulesdataaccess.exists() || !jrulessynchronization.exists() || !wbemodel.exists()) {
            println("[Error] If jar path is not specified, jrules-*.jars must be copied into the plugin '/lib' directory.")
            println("[Solution] Please specify a jar path to the jrules-*.jar and wbemodel.jar location, or copy them into the plugin lib directory.")
            println("[Solution] The following plugins are required to run ODM version imports:")
            !jrulesrs4j.exists()? println("[Info] Missing jrules-dataaccess-rs4j.jar") : println("[Info] Found jrules-dataaccess-rs4j.jar")
            !jrulesengine.exists()? println("[Info] Missing jrules-engine.jar") : println("[Info] Found jrules-engine.jar")
            !jruleslanguage.exists()? println("[Info] Missing jrules-language.jar") : println("[Info] Found jrules-language.jar")
            !jrulesdataaccess.exists()? println("[Info] Missing jrules-model-dataaccess.jar") : println("[Info] Found jrules-model-dataaccess.jar")
            !jrulesartifacts.exists() ? println("[Info] Missing jrules-ruleartifacts.jar") : println("[Info] Found jrules-ruleartifacts.jar")
            !jrulessynchronization.exists()? println("[Info] Missing jrules-synchronization.jar") : println("[Info] Found jrules-synchronization.jar")
            !jrulesteamserver.exists()? println("[Info] Missing jrules-teamserver.jar") : println("[Info] Found jrules-teamserver.jar")
            !wbemodel.exists()? println("[Info] Missing wbemodel.jar.") : println("[Info] Found wbemodel.jar.")
            System.exit(1)
        }
        else {
            println("[Info] Found jrules jars. Loading into classpath from plugin lib directory.")
        }
    }
    loadAllJars(PLUGIN_HOME + File.separator + 'lib')
}

private void loadAllJars(String directory) {
    File dir = new File(directory)
    if (!dir.isDirectory()) {
        println ("[Error] Directory ${dir.getAbsolutePath()} does not exist.")
        System.exit(1)
    }
    dir.list().each { child ->
        File childFile = new File(directory + File.separator + child)
        if (childFile.isDirectory()) {
            loadAllJars(childFile.getAbsolutePath())
        }
        def extension = ''
        def index = child.lastIndexOf('.')
        if (index && index > 0) {
            extension = child.substring(index + 1)
        }
        if (extension.equals('jar')) {
            if (childFile.isFile()) {
                this.getClass().classLoader.rootLoader.addURL(childFile.toURL())
            }
        }
    }
}

XTrustProvider.install()

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

final File workDir = new File('.').canonicalFile

// clean working directory
for(File file: workDir.listFiles()) {
    if (!file.isDirectory()) {
        file.delete()
    }
}

String odmUrl = props['odmUrl'].trim()
String username = props['username'].trim()
String password = props['password']
String project = props['project']?.trim()
String branch = props['branch']?.trim()
String deployment = props['deployment']?.trim()
String target = props['target']?.trim()
boolean isUseVFS = Boolean.valueOf(props['isUseVFS'])
String componentName = props['componentName']?.trim()
String datasource = props['datasource']?.trim()
String snapshot = props['snapshot']?.trim()
String namePattern = props['namePattern']?.trim()
String nextNumberString = props['nextNumber']?.trim()
Integer nextNumber

if (nextNumberString) {
    try {
        nextNumber = Integer.valueOf(nextNumberString)
    }
    catch (NumberFormatException e) {
        throw new RuntimeException("The configured next version number '"+
        nextNumberString+"' is not a valid integer.")
    }
}

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

// Set up UCD server clients
String UDUsername = 'PasswordIsAuthToken'
String UDPassword = String.format('{\"token\": \"%s\"}', System.getenv('AUTH_TOKEN'))
URI ucdServerUrl = new URI(System.getenv('AH_WEB_URL'))
VersionClient versionClient = new VersionClient(ucdServerUrl, UDUsername, UDPassword)
ComponentClient componentClient = new ComponentClient(ucdServerUrl, UDUsername, UDPassword)

while (odmUrl.endsWith('/')) {
    odmUrl = odmUrl.substring(0, odmUrl.length() - 1)
}
URI odmRepoURI = new URI(odmUrl + '/teamserver')

if (branch == "") {
    println ("[Info] No branch specified, using 'main' branch")
    branch = "main"
}

boolean redeploy = false

def getArchiveViaJavaClient = {
    importOdmJars(jarPath)

    //Download file to working directory to extract name information
    AntBuilder ant = new AntBuilder()
    ant.taskdef name: "ruleappExtractor", classname: "com.urbancode.air.plugin.odm.RuleAppExtractorTask"

    println ("[Action] Extracting RuleApp from ${odmUrl}")
    if (!snapshot) {
        if (nextNumberString && namePattern) {
            namePattern = namePattern + '-'
        }
        snapshot = namePattern + String.valueOf(nextNumber)
        println ("[Action] Creating ODM snapshot (baseline): ${snapshot}")
        ant.ruleappExtractor(
                server: odmRepoURI.toString(),
                datasource: datasource,
                user: username,
                password: password,
                project: project,
                branch: branch,
                deployment: deployment,
                target: target,
                baseline: snapshot,
                fromBaseline: ''
                )
    }
    else {
        redeploy = true
        println ("[Action] Extracting from ODM snapshot (baseline): ${snapshot}")
        ant.ruleappExtractor(
                server: odmRepoURI.toString(),
                datasource: datasource,
                user: username,
                password: password,
                project: project,
                branch: branch,
                deployment: deployment,
                target: target,
                baseline: '',
                fromBaseline: snapshot
                )
    }
}

def getArchiveViaRestApi = { OdmRestClient client ->
    def odmDecisionService = client.getDecisionService(project)
    def odmDeployment = client.getDeployment(odmDecisionService.id, deployment)
    def filename = odmDeployment.ruleAppName + '-' + odmDeployment.ruleAppVersion + '-' + (snapshot ?: "${namePattern}-${nextNumber}") + '.jar'

    client.downloadRuleappArchive(odmDeployment.id, filename)
}

def odmRestClient
try {
    odmRestClient = new OdmRestClient(airTool)
    println 'REST API found'
    println()
} catch (Exception e) {
    println e.getMessage()
    println()
}

if (odmRestClient) {
    getArchiveViaRestApi(odmRestClient)
} else {
    getArchiveViaJavaClient()
}

String ruleAppName = ''
String versionName = snapshot
String extension = ''
String majorMinorVersion = ''

//Extract ruleAppExtractor file data to set version properties
if (workDir.listFiles().length > 1) {
    println ("[Error] Unexpected files in working directory: " + workDir.getAbsolutePath())
    System.exit(1)
}
workDir.list().each { child ->
    File ruleAppJarFile = new File(File.separator + child)
    ruleAppName = ruleAppJarFile.name.split("-")[0]
    versionName = ruleAppJarFile.name
    def index = child.lastIndexOf('.')
    if (index && index > 0) {
        extension = child.substring(index + 1)
    }
    if (!extension.equals('jar')) {
        println ("[Error] Unexpected file type extension: " + extension)
        System.exit(1)
    }
    else {
        versionName = versionName.replaceFirst(java.util.regex.Pattern.quote(ruleAppName+"-"), "")
        majorMinorVersion = versionName.substring(0, versionName.indexOf("-"))
        versionName = versionName.replaceAll(java.util.regex.Pattern.quote("."+extension), "")
        //rename jar file to more common name
        boolean rename = new File(ruleAppJarFile.name).renameTo(new File(ruleAppName + ".jar"))
        if (!rename) {
            println("[Warning] Failed to rename RuleApp jar file")
        }
    }
}

if (!redeploy && nextNumberString) {
    nextNumber++
}

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 downloadFile = { version ->
    try {
        IO.copy(new File('.'), getTempDir(version))
        return getTempDir(version)
    }
    catch (Exception e) {
        System.err.println(String.format("Error downloading file: ", e.getMessage()))
    }
}

def downloadPackage = { version ->
    println ("[Action] Downloading archive '" + ruleAppName + ".jar' from Decision Services project '" + project + ":" + branch + "'")
    UUID result = null
    File tempDir = null
    String[] includes = ['**/*'] as String[]
    String[] excludes = [] as String[]
    try {
        boolean preserveExecutePermissions = Boolean.valueOf(props['saveFileExecuteBits'])
        versions = componentClient.getComponentVersions(componentName.toString(), false)
        componentClient.updateSourceConfigProperty(componentName, "nextNumber", nextNumber.toString(), "", false)

        println ('[Action] Creating a new component version: ' + version.toString())
        String versionId = versionClient.createVersion(componentName, version.toString(), ' ', true).toString()
        airTool.setOutputProperty('VersionID', versionId)
        if (isUseVFS) {
            tempDir = downloadFile(version)
            versionClient.addVersionFiles(componentName, versionId, tempDir, '', includes, excludes, preserveExecutePermissions, true, charset, extensions)
            try {
                versionClient.setVersionProperty(versionName, componentName, 'ruleAppVersion', majorMinorVersion, false)
            }
            catch (IOException ex) {
                println ("[Warn] Unable to assign 'ruleAppVersion' component version property: " + ex.getMessage())
            }
        }
        else {
            println (String.format('Not uploading version %s to CodeStation because using VFS was not selected.',
                    versionId))
        }
        versionClient.markImportFinished(componentName, versionId);
    }
    catch (Exception e) {
        println ("[Error] Error creating a new version: %s" + e.printStackTrace())
        System.exit(1)
    }
    finally {
        airTool.storeOutputProperties()
        try {
            if (tempDir != null && tempDir.exists()) {
                IO.delete(tempDir)
            }
        }
        catch (IOException e) {
            println ("Unable to delete download directory: " + e.getMessage())
        }
    }
    return result
}

downloadPackage(versionName)


