/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Build
 * IBM UrbanCode Deploy
 * IBM UrbanCode Release
 * IBM AnthillPro
 * (c) Copyright IBM Corporation 2016. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 */
import com.urbancode.air.AirPluginTool
import com.urbancode.air.CommandHelper
import com.urbancode.air.plugin.iisconfiguration.helper.MSDeployCmdLineHelper
import com.urbancode.air.plugin.iisconfiguration.helper.ResourceHelper
import com.urbancode.air.plugin.iisconfiguration.helper.AppCmdCLIHelper
import com.urbancode.air.plugin.iisconfiguration.helper.PowerShellHelper
import org.codehaus.jettison.json.JSONObject
import org.codehaus.jettison.json.JSONArray
import groovy.util.AntBuilder
import com.urbancode.air.plugin.iisconfiguration.Globals
import com.urbancode.air.plugin.iisconfiguration.Util
import groovy.io.FileType

println("Starting configure...")
final File PLUGIN_HOME = new File(System.getenv().get("PLUGIN_HOME"))
String DISCOVERY_PS_SCRIPT = "${PLUGIN_HOME.getCanonicalPath()}\\fullDiscovery.ps1"

//initialize plugin tools
def s = File.separator
def apTool = new AirPluginTool(args[0], args[1])
def isWindows = apTool.isWindows
def props = apTool.getStepProperties()
def commandHelper = new CommandHelper(new File(".").canonicalFile)
def antBuilder = new AntBuilder()

//initialize ucd related variables
def ucdUsername = apTool.getAuthTokenUsername()
def ucdPassword = apTool.getAuthToken()
def ucdUri = new URI(System.getenv("AH_WEB_URL"))
def resourceHelper = new ResourceHelper(ucdUri, ucdUsername, ucdPassword)
def thisResourcePath = props['resourcePath']
def configOutputFolder = props['configOutputFolder']
def resourceRoleName = props['resourceRoleName']
def resourceName = props['resourceName']

//initialize iis related variables
def appCmdPath = props['appCmdPath']
def msDeployHelper = new MSDeployCmdLineHelper()
def appCmdHelper = new AppCmdCLIHelper(appCmdPath)
def webDeployPath = props['webDeployPath']
def verb = "sync"
def srcType = ""
def srcPath = ""
def destType = "package"
def destPath = new File("iisConfiguration.zip").getCanonicalPath()
File sourcePathDir = new File(props['sourcePath'])
def argStrings = props['argString']?.split('\n')

//convert json to a properties map
def jsonToPropMap = { JSONObject jsonObj ->
    def result = [:]
    jsonObj.keys().each{
        result[it] = jsonObj.get(it)
    }

    return result
}

//convert properties map into json
def propMapToJson = {propMap ->
    JSONObject resourceRoleProps = new JSONObject()
    return propMap.each { key, value -> resourceRoleProps.put(key, value) }
}

//merge first jsonarray contents into the second one
def mergeJSONArrays = {JSONArray jsonArrayInput, JSONArray result ->
    for (int i = 0; i < jsonArrayInput.length(); i++) {
        result.put(jsonArrayInput.get(i))
    }
}

//bundle iis objects into a JSONArray
def extractToJSONArray = { iisObjectMap, thisParentPath, roleName, description  ->
    JSONArray result = new JSONArray()

    iisObjectMap.each{ name,  propMap ->
        def skipIteration = false
        JSONObject resourceRoleProps = propMapToJson(propMap)

        JSONObject newResourceJson = new JSONObject()
        newResourceJson.put("name", name)
        newResourceJson.put("description", description)
        newResourceJson.put("parent", thisParentPath)
        newResourceJson.put("role", roleName)
        newResourceJson.put("path", "${thisParentPath}/${name}")
        newResourceJson.put("roleProperties", resourceRoleProps)

        //if this resource is an Application,  we need to place it underneath
        //the associated Site on the resource tree
        if (roleName == Globals.APP_ROLE_NAME){
            //alter path to be underneath the associated Site
            newResourceJson.put("path", "${thisResourcePath}/Sites/${name}")

            //name should be the last part of the application path which is the
            //name for all applications
            name = (name =~ /.*\/(.*)/)[0][1]
            if (name.trim().isEmpty() || name == null) {
                //skip due to name conflicts between sites and applications
                skipIteration = true
            }
            else {
                //parent should be the path before the trailing name '(.*)/<name>'
                thisParentPath = (newResourceJson.get("path") =~ /(.*)\//)[0][1]
                newResourceJson.put("parent", thisParentPath)
                newResourceJson.put("name", name)
            }
        }

        if (!skipIteration) {
            result.put(newResourceJson)
        }
    }

    return result
}

//add iis objects to the resource tree
def addResourcesToResourceTree = { JSONArray resources ->
    for (i = 0; i < resources.length(); i++){
        //load props from json and create a base resource
        resourceJsonObject = resources.get(i)
        resourceName = resourceJsonObject.get("name").trim()
        resourcePath = resourceJsonObject.get("path")
        resourceParent = resourceJsonObject.get("parent")
        resourceDescription = resourceJsonObject.get("description")
        resourceRole = resourceJsonObject.get("role")
        resource = resourceHelper.getOrCreateSubResource(resourcePath, resourceParent, resourceName, resourceDescription)

        //now add role properties to the resource we just created
        resourceRoleProps = jsonToPropMap(resourceJsonObject.get("roleProperties"))
        resourceHelper.addRoleToResource(resource, resourceRole, resourceRoleProps)
    }
}

//MAIN FUNCTION
def main = {
    if (sourcePathDir == null || sourcePathDir.toString().isEmpty()){
      sourcePathDir = new File("C:\\inetpub\\wwwroot\\")
    }
    def srcResourcePaths = sourcePathDir.listFiles()
    JSONArray fullJSON = new JSONArray()

    //inside the if is when the user has selected Actions > Configure from the
    //resource tree and wants to create a topology in the resource tree
    //the else handles what happens when we run this step in a process
    //which performs a configuration discovery
    if (thisResourcePath && !configOutputFolder) {
        //if we are here then populate the resource tree with a topology
        //---SITES---
        def sites = appCmdHelper.listObjects(AppCmdCLIHelper.SITES)
        def sitesParentGroupName = "Sites"
        def sitesParentGroupPath = "${thisResourcePath}/${sitesParentGroupName}"
        JSONArray sitesJSON = extractToJSONArray(sites, sitesParentGroupPath, Globals.SITE_ROLE_NAME, "")

        //---APPS---
        def apps = appCmdHelper.listObjects(AppCmdCLIHelper.APPLICATIONS)
        def appsParentsGroupName = "Applications"
        def appsParentsGroupPath = "${thisResourcePath}/${appsParentsGroupName}"
        println("Found apps: ${apps}")
        JSONArray appsJSON = extractToJSONArray(apps, appsParentsGroupPath, Globals.APP_ROLE_NAME, "")

        //---APP POOLS---
        def appPools = appCmdHelper.listObjects(AppCmdCLIHelper.APPLICATION_POOLS)
        def appPoolsParentsGroupName = "Application Pools"
        def appPoolsParentsGroupPath = "${thisResourcePath}/${appPoolsParentsGroupName}"
        println("Found appPools: ${appPools}")
        JSONArray appPoolsJSON = extractToJSONArray(appPools, appPoolsParentsGroupPath, Globals.APP_POOL_ROLE_NAME, "")

        //create a Sites group to place all Sites underneath
        def sitesParentGroupResource = resourceHelper.getOrCreateSubResource(sitesParentGroupPath, thisResourcePath, sitesParentGroupName, "")
        addResourcesToResourceTree(sitesJSON)

        //add Apps underneath the associated Sites
        addResourcesToResourceTree(appsJSON)

        //add App Pools to resource tree to play all App Pools underneath
        def appPoolsParentsGroupResource = resourceHelper.getOrCreateSubResource(appPoolsParentsGroupPath, thisResourcePath, appPoolsParentsGroupName, "")
        addResourcesToResourceTree(appPoolsJSON)

        //build master json for topology discovery
        mergeJSONArrays(sitesJSON, fullJSON)
        mergeJSONArrays(appsJSON, fullJSON)
        mergeJSONArrays(appPoolsJSON, fullJSON)
        println("Configuration: \n${fullJSON.toString(4)}")
    }
    else {
        //if we are here, then perform a configuration discovery
        //retrieve srcType and srcPath
        def srcTypeAndPath = Util.formatPathAndTypeFromResourceRoleName(resourceRoleName, thisResourcePath)
        srcType = srcTypeAndPath['type']
        srcPath = srcTypeAndPath['path']

        //run msdeploy to perform package sync
        //example: msdeploy -verb:sync -source:webServer -dest:package=C:\testingArchive.zip
        msDeployHelper.runMSDeployScript(webDeployPath, verb, srcType, destType, srcPath, "\"${destPath}\"", argStrings)

        //unzip the config we just created so we can insert our metadata
        def configOutputFolderFile = new File(configOutputFolder)
        antBuilder.unzip(   dest:configOutputFolderFile.getCanonicalPath(),
                            src:destPath,
                            overwrite:"true" )

        //insert metadata
        def configOutputFolderName = configOutputFolder
        if (configOutputFolder.contains("/") || configOutputFolder.contains("\\")){
           configOutputFolderName = (configOutputFolder =~ /.*\/(.*)/)[0][1]
        }
        Properties metaData = new Properties()
        File metaDataFile = new File("${configOutputFolderFile.getCanonicalPath()}\\${Globals.UCD_METADATA_FILE_NAME}")
        metaData.setProperty('resourceRoleName', resourceRoleName)
        metaData.setProperty('configOutputFolder', configOutputFolderName)
        metaData.store(metaDataFile.newWriter(), null)

        //cleanup zip
        new File(destPath).delete()
    }
}

//on y va!
main()