/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Release
 * (c) Copyright IBM Corporation 2015. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 */
package com.urbancode.air.plugin.automation

import groovy.json.JsonBuilder
import groovy.json.JsonSlurper

import com.urbancode.air.plugin.hp.alm.rest.ALMRestHelper

class ALMHelper extends ALMRestHelper {
    protected def queryList  // list of all type names to query for

    public ALMHelper(def props) {
        super(props)
        def jsonQueryTypes = props['almQuery']  // JSON string

        if (jsonQueryTypes) {
            queryList = new JsonSlurper().parseText(jsonQueryTypes)
        }
    }

    // return a map of requirements with the specified requirement type
    protected def getRequirements() {
        def requirementsMap = [:]

        // if any requirement type is specified, acquire their ids
        if (isRequirements()) {
            def reqTypes = getTypesMap("requirement") // map of requirement type ids to names
            def typeIds = [] // list of ids to query for

            for (def query : queryList) {
                // defect is a separate entity that doesn't have a type id.. it must be queried for separately
                if (query == "Defect") {
                    continue
                }

                def typeId
                println(queryList)

                // iterate through each requirement type to acquire id
                for (def type : reqTypes) {
                    if (type.value == query) {
                        typeId = type.key
                        break
                    }
                }

                if (typeId) {
                    typeIds.add(typeId)
                }
            }

            String idStatement = typeIds.join(" or ")  // or statement if multiple ids are included
            String queryStatement = "type-id[${idStatement}]"
            String fieldStatement = "status,req-priority,target-rel,last-modified,type-id,name,description,comments"  // only the required fields are queried for
            requirementsMap = getQueryResponse("requirements", fieldStatement, queryStatement)
        }

        return requirementsMap
    }

    protected def getDefects() {
        def defectsMap = [:]

        if (isDefects()) {
            defectsMap = getEntitiesMap("defects")
        }
        return defectsMap
    }


    // return true if a requirement type is specified in the query list
    private boolean isRequirements() {
        // return false if the only query type specified is 'Defect'
        return queryList != null && !queryList.isEmpty() && !queryList.contains("Defect") || queryList.size() > 1
    }

    // return true if a defect type is specified in the query list
    private boolean isDefects() {
        return queryList.contains("Defect")
    }

    protected def getReleases() {
        return buildEntityResource("releases")
    }

    // return domains list formatted for urbancode release server
    private def getDomains() {
        return buildSiteResource("/rest/domains")
    }

    // return projects list formatted for urbancode release server
    private def getProjects() {
        return buildSiteResource("/rest/domains/${domain}/projects")
    }

    // return requirement statuses formatted for urbancode release server
    private def getReqStatuses() {
        return buildEntityListMapping("requirement", "Coverage Status")
    }

    private def getReqSeverities() {
        return buildEntityListMapping("requirement", "Priority")
    }

    private def getDefectStatuses() {
        return buildEntityListMapping("defect", "Bug Status")
    }

    private def getDefectSeverities() {
        return buildEntityListMapping("defect", "Severity")
    }

    private def getRequirementTypes() {
        def typesMap = getTypesMap("requirement")
        def types = [] as List

        for (def type : typesMap) {
            def entry = new Resource([value:type.value, label:type.value])
            types.add(entry)
        }

        // add defects as a type to allow user to map defect to a type in ucr
        types.add(new Resource([value:"Defect", label:"Defect"]))

        return buildJsonString(types)
    }

    // build a json string of a site resource formatted for use with the urbancode release server
    private String buildSiteResource(String resourcePath) {
        def gpathRoot = getSiteResource(resourcePath)
        def resources = [] as List
        def resourceProps

        // check whether the resource path references projects or domains
        // XML
        boolean xml = false
        if (gpathRoot.Project != null && gpathRoot.Project.size() > 0) {
            resourceProps = gpathRoot.Project
            xml = true
        }
        else if (gpathRoot.Domain != null && gpathRoot.Domain.size() > 0) {
            resourceProps = gpathRoot.Domain
            xml = true
        }
        // JSON
        else if (gpathRoot.Projects != null && gpathRoot.Projects.Project.size() > 0) {
            resourceProps = gpathRoot.Projects.Project
        }
        else if (gpathRoot.Domains != null && gpathRoot.Domains.Domain.size() > 0) {
            resourceProps = gpathRoot.Domains.Domain
        }
        else {
            throw new RuntimeException("No domains or projects exist on this site: ${serverUrl}")
        }

        for (def resource : resourceProps) {
            // gpath reference to specific attribute
            def resourceName
            if (xml) {
                resourceName = resource.@Name
            }
            else {
                resourceName = resource.Name
            }
            def entry = new Resource([value:resourceName, label:resourceName])
            resources.add(entry)
        }

        return buildJsonString(resources)
    }

    // build a json string from an entity resource formatted for use with the urbancode release server
    private String buildEntityResource(String resource) {
        def resourceMap = getEntitiesMap(resource)
        def resources = [] as List

        for (def entity : resourceMap) {
            def resourceName = entity.name
            def entry = new Resource([value:resourceName, label:resourceName])
            resources.add(entry)
        }

        return buildJsonString(resources)
    }

    // build a json string of liststhat exist on an entity in the alm project formatted for the ucr server
    private String buildEntityListMapping(String entityType, String listName) {
        def requirementLists = getListsMap(entityType) // all preset property lists for the entity
        def propValues = requirementLists.get(listName) // property values for a given list
        def formattedLists = [] as List

        for (def property : propValues) {
            def entry = new Resource([value:property, label:property])
            formattedLists.add(entry)
        }

        return buildJsonString(formattedLists)
    }

    // build a json string from a list of properties
    private String buildJsonString(def properties) {
        JsonBuilder builder = new JsonBuilder(properties)
        return builder.toString()
    }

    // parse json returned from a UCR mapping box
    protected def parsePropertyMapping(def jsonMapping) {
        def mappingSlurper = new JsonSlurper()  // slurper to parse alm property to ucr property mappings
        return mappingSlurper.parseText(jsonMapping)
    }
}

// class used to format entries to be represented properly in json
class Resource {String value; String label}