/*
* Licensed Materials - Property of IBM* and/or HCL**
* UrbanCode Deploy
* UrbanCode Build
* UrbanCode Release
* AnthillPro
* (c) Copyright IBM Corporation 2011, 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
*/
package com.urbancode.air.plugin.udeploy.components

import com.urbancode.air.AirPluginTool
import com.urbancode.ud.client.ComponentClient

import java.util.UUID

import org.codehaus.jettison.json.JSONArray
import org.codehaus.jettison.json.JSONObject
import org.codehaus.jettison.json.JSONException

public class ComponentHelper {
    def apTool
    def props = []
    def udUser
    def udPass
    def weburl
    ComponentClient client

    public ComponentHelper(def apToolIn) {
        apTool = apToolIn
        props = apTool.getStepProperties()
        udUser = apTool.getAuthTokenUsername()
        udPass = apTool.getAuthToken()
        weburl = System.getenv("AH_WEB_URL")
        client = new ComponentClient(new URI(weburl), udUser, udPass)

        com.urbancode.commons.util.ssl.XTrustProvider.install()
    }

    public def createComponent() {
        def compName = props['component']
        def description = props['description']
        def templateName = props['template']
        def templateVersion
        def sourceConfigType = props['sourceConfig']
        def defaultVersionType = props['defaultVersionType']
        def importAutomatically = Boolean.valueOf(props['importAutomatically'])
        def useVfs = Boolean.valueOf(props['useVfs'])
        def unparsedSCMProps = props['sourceConfigProps']

        if (!compName) {
            throw new IllegalArgumentException("no component name was specified")
        }
        if (!defaultVersionType) {
            throw new IllegalArgumentException("no version type was specified")
        }
        if (!description) {
            description = ""
        }
        if (!templateName) {
            templateName = ""
        }
        try {
            templateVersion = Integer.valueOf(props['templateVersion'])
        }
        catch(NumberFormatException e) {
            templateVersion = 0
        }

        if (!unparsedSCMProps) {
            UUID compUUID = client.createComponent(compName, description, sourceConfigType,
                defaultVersionType, templateName, templateVersion, importAutomatically, useVfs)
            apTool.setOutputProperty("component.id", compUUID.toString())
        }
        else {
            Map<String, String> parsedProps = parseSCMProperties(unparsedSCMProps, sourceConfigType);
            UUID compUUID = client.createComponent(compName, description, sourceConfigType,
                defaultVersionType, templateName, templateVersion, importAutomatically,
                useVfs, parsedProps)
            apTool.setOutputProperty("component.id", compUUID.toString())
        }
        println "created component with name ${compName}"

        apTool.setOutputProperties()
    }

    public def createMultipleComponents() {
        String json = props['json']
        JSONArray compsJson
        File jsonFile = new File(json)

        try {
            if (jsonFile.isFile()) {
                compsJson = new JSONArray(jsonFile.getText('UTF-8'))
            }
            else {
                compsJson = new JSONArray(json)
            }
        }
        catch (JSONException ex) {
            println("[Error] A syntax error occurred while parsing the JSON object.")
            throw ex
        }

        def compUUIDs = []

        for (int i = 0; i < compsJson.length(); i++) {
            UUID result
            JSONObject compJson = compsJson.get(i)
            String name, description, sourceConfigPlugin, defaultVersionType, templateName
            boolean importAutomatically, useVfs
            int templateVersion


            if (compJson.has("name")) {
                name = compJson.getString("name")
            }
            else {
                throw new IllegalArgumentException("A required field 'name' was not found in the following JSON object:\n"
                    + compJson)
            }
            if (compJson.has("description")) {
                description = compJson.getString("description")
            }
            if (compJson.has("sourceConfigPlugin")) {
                sourceConfigPlugin = compJson.getString("sourceConfigPlugin")
            }
            if (compJson.has("defaultVersionType")) {
                defaultVersionType = compJson.getString("defaultVersionType")
            }
            if (compJson.has("templateName")) {
                templateName = compJson.get("templateName")
            }
            if (compJson.has("importAutomatically")) {
                importAutomatically = compJson.getBoolean("importAutomatically")
            }
            if (compJson.has("useVfs")) {
                useVfs = compJson.getBoolean("useVfs")
            }
            if (compJson.has("templateVersion")) {
                templateVersion = compJson.getInt("templateVersion")
            }

            println("[Action] Creating new component '${name}'...")

            if (compJson.has("properties")) {
                def sourceConfigProperties = [:]
                JSONObject propsJson = compJson.get("properties")

                for (def propKey : propsJson.keys()) {
                    sourceConfigProperties << [propKey : propsJson.getString(propKey)]
                }

                result = client.createComponent(name, description, sourceConfigPlugin, defaultVersionType,
                    templateName, templateVersion, importAutomatically, useVfs, sourceConfigProperties)
            }
            else {
                result = client.createComponent(name, description, sourceConfigPlugin, defaultVersionType,
                    templateName, templateVersion, importAutomatically, useVfs)
            }

            if (!result) {
                throw new RuntimeException("Failed to create new component using JSON:\n${compJson}")
            }

            println("[Ok] Successfully created component '${name}'.")

            compUUIDs << result
        }

        apTool.setOutputProperty("component.ids", compUUIDs.join(','))
        apTool.storeOutputProperties()
    }

    public def deleteComponent() {
        def compName = props['component']

        if (!compName) {
            throw new IllegalArgumentException("no component was specified")
        }

        UUID compUUID = client.deleteComponent(compName)
        println "Deleted component: " + compName
        apTool.setOutputProperty("component.id", compUUID.toString())
        apTool.setOutputProperties()
    }

    public def setComponentProperty() {
        def componentName = props['component']
        def propName = props['name']
        def propValue = props['value'] ?: ''
        def isSecure = Boolean.valueOf(props['isSecure'])
        if (isSecure) {
            propValue = props['secureValue'] ?: propValue // default to 'value' if 'secureValue' not given
        }

        if (!propName) {
            throw new IllegalArgumentException("no property name was specified")
        }
        if (!componentName) {
            throw new IllegalArgumentException("no component was specified")
        }

        client.setComponentProperty(componentName, propName, propValue, isSecure)
    }

    public def getApplicationsForComponent() {
        def componentName = props['component']

        def appJson = client.getComponentApplications(componentName)

        String appNames = "";
        String appIds = "";
        String appCount = 0;
        for (int i = 0; i <appJson.length(); i++) {
            appCount++;

            if (appIds.length() > 0) {
                appIds += ",";
            }
            if (appNames.length() > 0) {
                appNames += ",";
            }

            def appJsonEntry = appJson.getJSONObject(i);
            def appId = appJsonEntry.getString("id");
            def appName = appJsonEntry.getString("name");
            println "Found application \"" + appName + "\" with ID " + appId

            appIds += appId;
            appNames += appName;
        }

        if (appCount == 0) {
            println "No applications found."
        }

        apTool.setOutputProperty("applicationsIds", appIds)
        apTool.setOutputProperty("applicationsNames", appNames)
        apTool.setOutputProperty("applicationsCount", appCount.toString())
        apTool.setOutputProperties()
    }

    public def createComponentRole() {
        def roleName = props['role']

        if (!roleName) {
            throw new IllegalArgumentException("no component was specified")
        }

        UUID roleUUID = client.createComponentRole(roleName)
        println "component role created with name: " + roleName
        apTool.setOutputProperty("component.role.id", roleUUID.toString())
        apTool.setOutputProperties()
    }

    public def addUserToRole() {
        def userName = props['user']
        def roleName = props['role']
        def component = props['component']

        if (!userName) {
            throw new IllegalArgumentException("no user was specified")
        }
        if (!roleName) {
            throw new IllegalArgumentException("no role was specified")
        }
        if (!component) {
            throw new IllegalArgumentException("no component was specified")
        }
        client.addUserToRoleForComponent(userName, roleName, component)
        println "Added user ${userName} to role ${roleName} for component ${component}"
    }

    public def addGroupToRole() {
        def groupName = props['group']
        def roleName = props['role']
        def component = props['component']

        if (!groupName) {
            throw new IllegalArgumentException("no group was specified")
        }
        if (!roleName) {
            throw new IllegalArgumentException("no role was specified")
        }
        if (!component) {
            throw new IllegalArgumentException("no component was specified")
        }
        client.addGroupToRoleForComponent(groupName, roleName, component)
        println "Added group ${groupName} to role ${roleName} for component ${component}"
    }

    public def addTagToComponent() {
        def componentName = props['component']
        def tagName = props['tag']

        if (!componentName) {
            throw new IllegalArgumentException("no component was specified")
        }
        if (!tagName) {
            throw new IllegalArgumentException("no tag was specified")
        }

        client.addTagToComponent(componentName, tagName)
        println("Added tag: ${tagName} to component: ${componentName}")
    }

    public def removeTagFromComponent() {
        def componentName = props['component']
        def tagName = props['tag']

        if (!componentName) {
            throw new IllegalArgumentException("no component was specified")
        }
        if (!tagName) {
            throw new IllegalArgumentException("no tag was specified")
        }

        client.removeTagFromComponent(componentName, tagName)
        println("Removed tag: ${tagName} from component: ${componentName}")
    }

    public def componentExists() {
        def component = props['component']

        if (!component) {
            throw new IllegalArgumentException("no component was specified")
        }

        try {
            def componentID = client.getComponentUUID(component)
            println "Component with name ${component} was found."
            apTool.setOutputProperty("exists", "true");
        }
        catch(IOException e) {
            if(e.getMessage().contains("404")) {
                println "Request was successful but no component with name ${component} was found."
                apTool.setOutputProperty("exists", "false");
            }
            else {
                println "An error occurred during your request."
                throw new IOException(e);
            }
        }
        apTool.setOutputProperties()
    }

    private Map<String, String> parseSCMProperties(def unparsedSCMProperties, def sourceConfigType) {
        Map<String, String> result = new HashMap<String, String>()
        String prefix = findSCMPropPrefix(sourceConfigType)

        String[] splitProps = unparsedSCMProperties.split("\n")
        for (String prop : splitProps) {
            String name = prop.substring(0, prop.indexOf("="))
            String value = prop.substring(prop.indexOf("=")+1)
            if (prefix && !name.startsWith(prefix + "/")) {
                name = prefix + "/" + name
            }
            result.put(name, value)
        }
        return result
    }

    private String findSCMPropPrefix(def sourceConfigType) {
        String result = "";
        if (sourceConfigType.equals("AnthillPro")) {
            result = "AnthillComponentProperties"
        }
        else if (sourceConfigType.equals("File System")) {
            result = "FileSystemComponentProperties"
        }
        else if (sourceConfigType.equals("File System (Versioned)")) {
            result = "FileSystemVersionedComponentProperties"
        }
        else if (sourceConfigType.equals("Git")) {
            result = "GitComponentProperties"
        }
        else if (sourceConfigType.equals("Jenkins")) {
            result = "JenkinsComponentProperties"
        }
        else if (sourceConfigType.equals("Luntbuild")) {
            result = "LuntbuildComponentProperties"
        }
        else if (sourceConfigType.equals("Maven")) {
            result = "MavenComponentProperties"
        }
        else if (sourceConfigType.equals("Perforce")) {
            result = "PerforceComponentProperties"
        }
        else if (sourceConfigType.equals("PVCS")) {
            result = "PVCSComponentProperties"
        }
        else if (sourceConfigType.equals("StarTeam")) {
            result = "StarTeamComponentProperties"
        }
        else if (sourceConfigType.equals("Subversion")) {
            result = "SubversionComponentProperties"
        }
        else if (sourceConfigType.equals("TFS")) {
            result = "TFSComponentProperties"
        }
        else if (sourceConfigType.equals("TeamCity")) {
            result = "TeamCityComponentProperties"
        }
        else if (sourceConfigType.equals("TeamForge")) {
            result = "TeamForgeComponentProperties"
        }
        else if (sourceConfigType.equals("TFS_SCM")) {
            result = "TFS_SCMComponentProperties"
        }

        if (result) {
            println "matched source config ${sourceConfigType} with property definition sheet"
        }
        return result;
    }

    public def addComponentToTeam() {
        def componentNames = (props['component'])?.split(',')*.trim()
        def teamName = props['team']
        def typeName = props['type']

        if (componentNames.size() == 0) {
            throw new IllegalArgumentException("no components were specified")
        }
        if (!teamName) {
            throw new IllegalArgumentException("no team was specified")
        }

        for (def componentName : componentNames) {
            client.addComponentToTeam(componentName, teamName, typeName)
            println "Component '${componentName}' was added to team for the given type classification."
        }
    }

    public def getComponentProperties() {
        def componentName = props['component']

        if (!componentName) {
            throw new IllegalArgumentException("no component was specified")
        }
        println "Retrieving component properties."
        Map<String, String> propMap = client.getComponentProperties(componentName)
        for(String key : propMap.keySet()) {
            String value = propMap.get(key)
            println "${key}: ${value}"
            apTool.setOutputProperty(key, value)
        }
        apTool.setOutputProperties()
        println "Component properties retrieved."
    }

    public def getComponentInfo() {
        def componentName = props['component']
        if (!componentName) {
            throw new IllegalArgumentException("no component was specified")
        }

        def componentJson = client.getComponent(componentName)
        def componentInfoMap = client.getJSONAsProperties(componentJson)
        for (String key : componentInfoMap.keySet()) {
            apTool.setOutputProperty(key, componentInfoMap.get(key))
        }
        apTool.setOutputProperties()

        println componentJson
    }
}
