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

/*
 * JIRA REST API Documentation: https://docs.atlassian.com/jira/REST/latest/
 */

import com.urbancode.air.AirPluginTool
import com.urbancode.air.plugin.jira.addcomments.FailMode
import com.urbancode.air.plugin.jira.addcomments.JIRAHelper
import com.urbancode.air.plugin.jira.addcomments.TextUtil
import groovy.json.JsonBuilder
import groovy.json.JsonException
import groovy.json.JsonSlurper
import org.apache.http.impl.client.DefaultHttpClient

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

final def serverUrl         = props['serverUrl']
final def username          = props['username']
final def password          = props['passwordScript']?:props['password']

final def issueIds          = TextUtil.textToList(props['issueIds'], true)
final def failMode          = FailMode.valueOf(props['failMode'])
def proxyHost = props['proxyHost']?.trim()
def proxyPort = props['proxyPort']?.trim()
boolean allowInsecure = (props['allowInsecure']?:"false").toBoolean()

def components             = TextUtil.textToList(props['components'])
def fixVersions            = TextUtil.textToList(props['fixVersions'])
def customFields           = props['customFields']?.trim()

//------------------------------------------------------------------------------
// Script content
//------------------------------------------------------------------------------

println "Server:\t$serverUrl"
println "Issue Keys:\t$issueIds"
println "Fail mode configured for this step is ${failMode}"
println ""

// Convert components list of strings to a list of maps for the REST call
components = components.collect{ [add: [name: it]] }

// Convert fixVersions list of strings to a list of maps for the REST call
fixVersions = fixVersions.collect{ [add: [name: it]] }

// Empty JIRAHelper class. Created to call helper methods
JIRAHelper jira = new JIRAHelper()

// Construct the client to allow the authentication of the user
DefaultHttpClient client
client = jira.createClient(username, password, proxyHost, proxyPort, allowInsecure)

// Keeps track of update errors
def errorCount = 0

// Parse custom fields
if (customFields) {
    try {
        customFields = new JsonSlurper().parseText(customFields)
        if (customFields instanceof List) {
            println "Expected Custom Fields to be a JSON object, but received a JSON array:"
            println()
            println new JsonBuilder(customFields)
            println()
            System.exit(1)
        }
    } catch (JsonException e) {
        println 'Failed to parse Custom Fields JSON:'
        println()
        println customFields
        println()
        System.exit(1)
    }
}

issueIds.each { issueId ->
    try {
        // Confirm the issue exists
        if (!jira.doesIssueExist(client, serverUrl, issueId)) {
            errorCount++
            throw new IllegalStateException("Specified Issue not found.")
        }

        // Get the Edit Issue metadata so we can determine what field types are allowed
        def editIssueMeta = jira.getEditIssueMeta(client, serverUrl, issueId)

        // Create a list of objects representing the allowed fields [{"id": String, "name": String, "required": boolean}...]
        def allowedFields = editIssueMeta.fields.collect { fieldId, fieldDef ->
            [id: fieldId, name: fieldDef.name, required: fieldDef.required]
        }

        def fields = [:]
        def update = [:]

        // Add custom fields to JSON
        customFields.each { key, value ->
            def id = allowedFields.find{
                it.name == key && it.id.startsWith('customfield_')
            }?.id

            // Fail if the given custom field name is not in the list of allowed fields for the given issue
            if (!id) {
                errorCount++
                throw new IllegalStateException("\"${key}\" is not an allowed custom field for the given issue.")
            }
            else {
                fields.put(id, value)
            }
        }

        // Add standard fields to JSON
        if (components)  { update.put('components', components) }
        if (fixVersions) { update.put('fixVersions', fixVersions) }

        // If fields and update maps are empty, there is nothing to edit, so skip the REST call
        if (!fields && !update) {
            println "Skipping edit of ${issueId} as there are no fields to update."
            println "--------------------------"
            return
        }

        def requestBody = ['fields': fields, 'update': update]

        // Change mapping to JSON and prepare it to be sent via REST call
        requestBody = new JsonBuilder(requestBody)

        // Invoke the edit issue REST call
        if (!jira.editIssue(client, serverUrl, issueId, requestBody)) {
            errorCount++
            throw new IllegalStateException("View above output for details.")
        }
    }
    catch (Exception e) {
        if (failMode == FailMode.FAIL_FAST) {
            println "Fail Fast: problem editing ${issueId}"
            e.printStackTrace()
            throw new IllegalStateException("Full execution of edit issue failed.")
        }

        println e.getMessage()
    }

    println "--------------------------"
}

//------------------------------------------------------------------------------
// Check post conditions
//------------------------------------------------------------------------------

// Check if no issues were specified
if (failMode == FailMode.FAIL_ON_NO_UPDATES && !issueIds) {
    throw new IllegalStateException("Fail on no Updates: No issues specified.")
}

// Check if all updates failed
if (failMode == FailMode.FAIL_ON_NO_UPDATES && errorCount == issueIds.size()) {
    throw new IllegalStateException("Fail on no Updates: All issues failed to update.")
}

if (failMode == FailMode.FAIL_ON_ANY_FAILURE && errorCount > 0) {
    throw new IllegalStateException("Fail On Any Failure: Got ${errorCount} failures!")
}