/**
* 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 transitionName        = props['transitionName']
final def additionalComment = props['additionalComment']
final def resolutionName    = props['resolutionName']
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 customFields            = props['customFields']?.trim()

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

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

// 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)
    }
}

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

        // Get the possible issue transitions
        def transitions = jira.getIssueTransitions(client, serverUrl, issueId)

        // Determine if specified transition is valid
        def transition = transitions.transitions.find{it.name == transitionName}
        if (!transition) {
            println "Could not find given transition for issue ${issueId}."
            println "Possible transition states: " + transitions.transitions*.name

            errorCount++
            throw new IllegalStateException("Specified Action \"${transitionName}\" was not available.")
        }

        // Issue exists and transition is valid
        // Create Update REST call body as HashMap object
        def updateIssueMap = [:]

        // Append the new transition state the issue will enter
        def transitionMap = [:]
        transitionMap.put("id", "${transition.id}")
        updateIssueMap.put("transition", transitionMap)

        def fieldsMap = [:]

        // Confirm resolutionName is valid, else default value will be assigned
        if (jira.isResolutionValid(client, serverUrl, issueId, resolutionName)) {
            def resolutionMap = [:]
            resolutionMap.put("name", resolutionName)
            fieldsMap.put("resolution", resolutionMap)
        }

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

        // Add custom fields
        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 this project and issue type
            if (!id) {
                errorCount++
                throw new IllegalStateException("Custom field \"${key}\" is not allowed for the given transition.")
            }
            else {
                fieldsMap.put(id, value)
            }
        }

        updateIssueMap.put("fields", fieldsMap)

        // Append a comment that will be posted with the updated transition
        if (additionalComment) {
            def addMap = [:]
            addMap.put("body", additionalComment)
            def commentMap = [:]
            commentMap.put("add", addMap)

            def updateMap = [:]
            def commentArray = []
            commentArray << commentMap
            updateMap.put("comment", commentArray)
            updateIssueMap.put("update", updateMap)
        }

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

        // Call the do transitions REST call
        if (!jira.transitionIssue(client, serverUrl, issueId, updateJSON)) {
            errorCount++
            throw new IllegalStateException("View above output for details.")
        }
    }
    catch (Exception e) {
        if (failMode == FailMode.FAIL_FAST) {
            println "Fail Fast: problem transitioning ${issueId}"
            e.printStackTrace()
            throw new IllegalStateException("Full execution of update 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!")
}