/*
 * Licensed Materials - Property of IBM* and/or HCL**
 * UrbanCode Deploy
 * (c) Copyright IBM Corporation 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 com.urbancode.air.AirPluginTool
import com.urbancode.air.ExitCodeException
import com.urbancode.air.plugins.accurev.AccuRevHelper
import com.urbancode.air.plugins.accurev.objects.Change
import com.urbancode.air.plugins.accurev.objects.Issue
import com.urbancode.air.plugins.accurev.objects.Stream
import com.urbancode.commons.util.IO
import com.urbancode.ud.client.ComponentClient
import com.urbancode.ud.client.VersionClient
import java.nio.charset.Charset
import java.text.SimpleDateFormat

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

Properties props = apTool.getStepProperties()

final Properties 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)
}

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)

boolean isUseVFS = Boolean.valueOf(props['isUseVFS'])
String componentName = props['componentName']
String serverUrl = props['serverUrl'].trim()
String username  = props['username'].trim()
String password  = props['password']
String depot     = props['depot'].trim()
String stream    = props['stream'].trim() ? props['stream'].trim() : depot
String accurev   = props['accurev'].trim()

boolean preserveExecutePermissions = Boolean.valueOf(props['saveFileExecuteBits'])

// Parse Includes Field
String includesString = props['includes']
String[] includes = new String[0];
if (includesString != null ) {
    if (includesString.trim().length() == 0) {
        includesString = "**/*";
    }
    includes = includesString.split(",|\n");
    for (int i=0; i<includes.length; i++) {
        includes[i] = includes[i].trim();
    }
}

// Parse Excludes Field
String excludesString = props['excludes']
String[] excludes = new String[0];
if (excludesString != null ) {
     excludes = excludesString.split(",|\n");
     for (int i=0; i<excludes.length; i++) {
         excludes[i] = excludes[i].trim();
    }
}

// Automatically Import Values
boolean importTransactions =  Boolean.valueOf(props['importTransactions'])
boolean importIssues       =  Boolean.valueOf(props['importIssues'])
boolean importSnapshots    =  Boolean.valueOf(props['importSnapshots'])

// Manual Import Values
String tag = props['tag'].trim()
Set<String> transNumsInput = props['transNums'].trim() ? props['transNums'].split(",")*.trim() - [""] : []
Set<String> issueNumsInput = props['issueNums'].trim() ? props['issueNums'].split(",")*.trim() - [""] : []
Set<String> snapshotNamesInput = props['snapshotNames'].trim() ? props['snapshotNames'].split(",")*.trim() - [""] : []
transNumsInput = transNumsInput.sort()
issueNumsInput = issueNumsInput.sort()
snapshotNamesInput = snapshotNamesInput.sort()

List<String> versions = componentClient.getComponentVersions(componentName.toString(), false)
List<String> archivedVersions = componentClient.getArchivedComponentVersions(componentName)
versions.addAll(archivedVersions)

Boolean fail = false

AccuRevHelper arh = null
try {
    arh = new AccuRevHelper(serverUrl, username, password, depot, stream, accurev)

    ////////////////////////////////////////////////////////////////
    // Confirm Transaction, Issues, and Snapshots exist if specified.
    // Else get latest (Transaction/Issue) and all Snapshots.
    ////////////////////////////////////////////////////////////////

    //////////////////////////////////////////
    // Confirm Promote Transaction Numbers exist
    //////////////////////////////////////////

    def transNumsImport  = []
    // Ignore if Workspace type
    if (arh.stream.isWorkspace()) {
        println "[Info] Ignoring Promote Transactions due to Stream type of Workspace."
    }
    // Identify Transaction Number to import
    else if (transNumsInput.size() > 0 || importTransactions) {
        // Retrieve all Promote Transaction numbers and date -> [TransNum:Date]
        def transNumsAccuRev = arh.getPromoteTransactionNumbers()
        if (transNumsAccuRev.size() == 0) {
            println ("[Info] No Promote Transaction Numbers found in AccuRev.")
            transNumsImport = []
        }
        // Confirm specified transNumsInput values exist
        else if (transNumsInput.size() > 0) {
            def newTransNums = []
            for (String input : transNumsInput) {
                transNumsAccuRev.each { it ->
                    if (it.key == input) {
                        newTransNums.add(it.key)
                    }
                }
            }
            println ("[Info] Manual Import - Promote Transaction Number Report:")
            println ("\tIdentified: " + newTransNums.toString())
            println ("\tUnidentified: " + (transNumsInput - newTransNums).sort().toString())
            transNumsImport = newTransNums
        }
        // Find latest transNum based on date
        else {
            transNumsImport = [transNumsAccuRev.max{ it.value }?.key]
            if (transNumsImport.size() == 1) {
                println ("[Info] Latest Promote Transaction Number: " + transNumsImport[0].toString())
            } else {
                println ("[Info] Unable to find the latest Promote Transaction Number.")
                transNumsImport = []
            }
        }
    } else {
        println "[Info] Ignoring Promote Transactions."
    }

    //////////////////////////////////////////
    // Confirm Issue versions exist
    //////////////////////////////////////////

    List<Issue> issuesImport = new ArrayList<Issue>()
    // Ignore if Workspace type
    if (arh.stream.isWorkspace()) {
        println "[Info] Ignoring Issues due to Stream type of Workspace."
    }
    // Identify Issue to Import
    else if (issueNumsInput.size() > 0 || importIssues) {
        // Retrieve all Issues as objects -> List<Issue> but no List<Change> info
        List<Issue> issuesAccuRev = arh.getStreamIssueList()
        if (issuesAccuRev.size() == 0) {
            println ("[Info] No Issues found.")
        // Confirm specified issueNum exists
        } else if (issueNumsInput.size() > 0) {
            List<Issue> newIssuesList = new ArrayList<Issue>()
            for (String i : issueNumsInput) {
                issuesAccuRev.each { it ->
                    if (it.getIssueNum() == i) {
                        newIssuesList.add(it)
                    }
                }
            }
            println ("[Info] Manual Import - Issue Report:")
            println ("\tIdentified: " + newIssuesList*.getIssueNum())
            println ("\tUnidentified: " + (issueNumsInput - newIssuesList*.getIssueNum()*.toString()).toString())
            issuesImport = newIssuesList
        // Find latest based on max Issue Number
        } else {
            Issue issue = issuesAccuRev.max{ Integer.valueOf(it.getIssueNum()) }
            if (issue) {
                println ("[Info] Latest Issue: " + issue.getIssueNum())
                issuesImport.add(issue)
            } else {
                println ("[Info] Unable to find an Issue.")
            }
        }
    } else {
        println "[Info] Ignoring Issues."
    }

    //////////////////////////////////////////
    // Confirm Snapshot versions exist
    //////////////////////////////////////////

    List<Stream> snapshotsImport = new ArrayList<Stream>()
    // Ignore if Workspace type
    if (arh.stream.isWorkspace()) {
        println "[Info] Ignoring Snapshots due to Stream type of Workspace."
    }
    // Identify Snapshot to Import
    else if (snapshotNamesInput.size() > 0 || importSnapshots) {
        // Retrieve all Snapshots as objects -> List<Stream>
        List<Stream> snapshotsAccuRev = arh.getSnapshots()
        if (snapshotsAccuRev.size() == 0) {
            println ("[Info] No Stream Snapshots found.")
        // Confirm specified Snapshot exists
        } else if (snapshotNamesInput.size() > 0) {
            for (String s : snapshotNamesInput) {
                snapshotsAccuRev.each { it ->
                    if (it.getName() == s) {
                        snapshotsImport.add(it)
                    }
                }
            }
            if (snapshotsImport.size() > 0) {
                println ("[Info] Identified Snapshot: " + snapshotsImport*.getName())
            } else {
                println ("[Info] Unable to identify Snapshot: " + (snapshotNamesInput - snapshotsImport*.getName()).toString())
            }
        // Import latest snapshot
        } else {
            Stream snapshot = snapshotsAccuRev.max{ Integer.valueOf(it.getTime()) }
            if (snapshot) {
                println ("[Info] Latest Snapshot: " + snapshot.getName())
                snapshotsImport.add(snapshot)
            } else {
                println ("[Info] Unable to find a Snapshot.")
            }
        }
    } else {
        println "[Info] Ignoring Snapshots."
    }


    ////////////////////////////////////////////////////////////////
    // Import identified Transaction, Issues, and Snapshots
    // Each loop checks if version already exists.
    ////////////////////////////////////////////////////////////////
    println ""
    println "[Action] Importing..."

    //////////////////////////////////////////
    // Import Promote Transaction Version(s)
    //////////////////////////////////////////
    transNumsImport.each { transNum ->
        File tempDir = null
        try {
            String versionName = tag ? transNum + " - ${tag}" : transNum // Append tag if found
            Boolean hasVersion = versions.contains(versionName)
            if (!hasVersion) {
                println ("\n[Action] Creating new Promote Transaction version ${versionName}...")
                tempDir = arh.getTempDir(versionName)
                arh.populateFolder(stream, transNum, tempDir) // Populate directory with Transaction files
                String versionId = versionClient.createVersion(componentName.toString(), versionName, "", true).toString()
                if (isUseVFS) {
                    versionClient.addVersionFiles(componentName.toString(), versionId, tempDir, "", includes, excludes, preserveExecutePermissions, true, charset, [""] as String[])
                } else {
                    println (String.format("[Info] Not uploading Transaction version %s to VFS because using VFS was not selected.",
                            versionId))
                }
                apTool.setOutputProperty("VersionID", versionId)
                versionClient.markImportFinished(componentName.toString(), versionId);
            } else {
                println (String.format("[Info] UCD already contains Transaction version %s. A duplicate will not be created.",
                        versionName))
            }
        } catch (Exception e) {
            System.err.println(String.format("[Error] Unable to create new Transaction version: %s", e.toString()))
            fail = true
        } finally {
            try {
                if (tempDir != null && tempDir.exists()) {
                    IO.delete(tempDir);
                }
            } catch (IOException e) {
                System.err.println(String.format("[Warning] Unable to delete download directory", e.getMessage()));
            }
        }
    }

    //////////////////////////////////////////
    // Import Issue Version(s)
    //////////////////////////////////////////
    issuesImport.each { issue ->
        File tempDir = null
        try {
            String versionName = tag ? issue.getVersionName() + " - ${tag}" : issue.getVersionName() // Append tag if found
            Boolean hasVersion = versions.contains(versionName)
            if (!hasVersion) {
                println ("\n[Action] Creating new ${versionName} version...")
                issue.setChanges(arh.getChanges(issue)) // Retrieve info on the Issue's Changeset
                tempDir = arh.getTempDir(versionName)
                // Iterate through each Changeset file and download to tempDir
                for (Change change : issue.getChanges()) {
                    // Issues may contain defunct files (files that have been removed from the workspace)
                    // This simple catch will ensure the entire import doesn't fail.
                    try {
                        arh.populateFileInIssue(change.getRealNamedVersion(), tempDir, change.getLocation())
                    } catch(ExitCodeException ex) {
                        println (String.format("[Warning] Unable to download file. Defunct files cannot be retrieved."))
                    }
                }
                String versionId = versionClient.createVersion(componentName.toString(), versionName, "", true).toString()
                if (isUseVFS) {
                    versionClient.addVersionFiles(componentName.toString(), versionId, tempDir, "", includes, excludes, preserveExecutePermissions, true, charset, [""] as String[])
                } else {
                    println (String.format("[Info] Not uploading Issue version %s to VFS because using VFS was not selected.",
                            versionId))
                }
                apTool.setOutputProperty("VersionID", versionId)
                versionClient.markImportFinished(componentName.toString(), versionId);
            } else {
                println (String.format("[Info] UCD already contains Issue version %s. A duplicate will not be created.",
                        versionName))
            }
        } catch (Exception e) {
            System.err.println(String.format("[Error] Unable to create new Issue version: %s", e.toString()))
            fail = true
        } finally {
            try {
                if (tempDir != null && tempDir.exists()) {
                    IO.delete(tempDir);
                }
            } catch (IOException e) {
                System.err.println(String.format("[Warning] Unable to delete download directory", e.getMessage()));
            }
        }
    }

    //////////////////////////////////////////
    // Import Snapshot Version(s)
    //////////////////////////////////////////
    snapshotsImport.each { snapshot ->
        File tempDir = null
        try {
            String versionName = tag ? snapshot.getName() + " - ${tag}" : snapshot.getName() // Append tag if found
            Boolean hasVersion = versions.contains(versionName)
            if (!hasVersion) {
                println ("\n[Action] Creating new Snapshot ${versionName} version...")
                tempDir = arh.getTempDir(versionName)
                arh.populateFolder(snapshot.getName(), tempDir)
                String versionId = versionClient.createVersion(componentName.toString(), versionName, "", true).toString()
                if (isUseVFS) {
                    versionClient.addVersionFiles(componentName.toString(), versionId, tempDir, "", includes, excludes, preserveExecutePermissions, true, charset, [""] as String[])
                } else {
                    println (String.format("[Info] Not uploading Snapshot version %s to VFS because using VFS was not selected.",
                            versionId))
                }
                apTool.setOutputProperty("VersionID", versionId)
                versionClient.markImportFinished(componentName.toString(), versionId);
            } else {
                println (String.format("[Info] UCD already contains Snapshot version %s. A duplicate will not be created.",
                        versionName))
            }
        } catch (Exception e) {
            System.err.println(String.format("[Error] Unable to create new Snapshot version: %s", e.toString()))
            fail = true
        } finally {
            try {
                if (tempDir != null && tempDir.exists()) {
                    IO.delete(tempDir);
                }
            } catch (IOException e) {
                System.err.println(String.format("[Warning] Unable to delete download directory", e.getMessage()));
            }
        }
    }

    //////////////////////////////////////////
    // Import Workspace Stream
    //////////////////////////////////////////
    if (arh.stream.isWorkspace()) {
        File tempDir = null
        try {
            String date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(new Date())
            String versionName = tag ? date + " - " + tag : date
            Boolean hasVersion = versions.contains(versionName)
            if (!hasVersion) {
                println ("\n[Action] Import workspace ${versionName}...")
                tempDir = arh.getTempDir(versionName)
                arh.populateFolder(stream, tempDir) // Populate directory with Workspace's current file set
                String versionId = versionClient.createVersion(componentName.toString(), versionName, "", true).toString()
                if (isUseVFS) {
                    versionClient.addVersionFiles(componentName.toString(), versionId, tempDir, "", includes, excludes, preserveExecutePermissions, true, charset, [""] as String[])
                } else {
                    println (String.format("[Info] Not uploading Workspace version %s to VFS because using VFS was not selected.",
                            versionId))
                }
                apTool.setOutputProperty("VersionID", versionId)
                versionClient.markImportFinished(componentName.toString(), versionId);
            } else {
                println (String.format("[Info] UCD already contains Workspace version %s. A duplicate will not be created.",
                        versionName))
            }
        } catch (Exception e) {
            System.err.println(String.format("[Error] Unable to create new Workspace version: %s", e.toString()))
            fail = true
        } finally {
            try {
                if (tempDir != null && tempDir.exists()) {
                    IO.delete(tempDir);
                }
            } catch (IOException e) {
                System.err.println(String.format("[Warning] Unable to delete download directory", e.getMessage()));
            }
        }
    }
} finally {
    apTool.storeOutputProperties()

    if (arh) {
        arh.logout()
    }
}

if (fail) {
    System.exit(1)
}

println ("Successfully imported new AccuRev version(s)!")
