/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Deploy
 * (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.
 */

package com.urbancode.air.plugins.autoscaler

import groovy.json.JsonException
import groovy.json.JsonSlurper
import org.apache.http.client.methods.HttpPut
import org.apache.http.client.utils.URIBuilder
import org.apache.http.entity.StringEntity
import org.apache.http.HttpEntity
import org.apache.http.HttpResponse
import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.util.EntityUtils
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder

public class AutoScaler extends CloudFoundry{

    def client      // DefaultHttpClient used to make REST calls
    def address     // Base path to the Auto-Scaling server
    def app_id      // The application's unique identifier

    AutoScaler(def address, def app) {
        super()
        this.address = address + "/v1/autoscaler"
        this.app_id  = getAppId(app)
        this.client  = new DefaultHttpClient()

        println "[Ok] Address: ${this.address}"
        println "[Ok] App_ID: ${app_id}"
    }

    /**
     * URL: https://<api server URL>/v1/autoscaler/apps/{app_id}/policy"
     * Adds a policy to the Application. Returns policyId if successful
     * param policy  String Policy being added to the Application
     * return        Returns policyId, else null response
     */
    private def addPolicy(def policy) {
        def scalingAddress = "${address}/apps/${app_id}/policy"
        println "[Ok] Full Address: ${scalingAddress}"

        def result = ""
        HttpResponse response = doHttpPut(scalingAddress, policy)
        def statusLine = response.getStatusLine()
        def statusCode = statusLine.getStatusCode()
        def output = httpResponse2Map(response)

        println ""
        if (statusCode == 200) {
            println "[Ok] Status Code: ${statusCode}"
            result = output.policyId
        }
        else if (statusCode == 401) {
            println "[Error] Status Code: ${statusCode} - Invalid Bearer Token. Please login via the CF CLI or the IBM Containers Login Step."
            println "[Error] The token is retrieved from <user_dir>/.cf/config.json."
            println "[Error] ${output}"
        }
        else if (statusCode == 404) {
            println "[Error] Status Code: ${statusCode} - Could not reach the server."
            println "[Error] ${output}"
            println "[Possible Solution] Confirm that the Scaling API URL and Application input are correct."
        }
        else {
            println "[Error] Status Code: ${statusCode} - Add Policy reached an unknown error."
            println "[Error] ${output}"
            println "[Possible Solution] Confirm that the Auto-Scaling service is installed on your IBM Bluemix space."
        }
        return result
    }

    /**
     * Calls a REST Put request and returns the HTTPResponse
     *
     * @param address   The complete URL address of the REST PUT call.
     * @param policy    The policy JSON that will be attached to the App
     * @return          The HTTPResponse for the PUT REST Call.
     */
    public HttpResponse doHttpPut(String address, String policy){
        // Create HttpPut Request and append Issue JSON
        URIBuilder uriBuilder = new URIBuilder(address)
        URI uri
        try {
            uri = uriBuilder.build()
        }
        catch (URISyntaxException ex) {
            throw new URISyntaxException("[Error] The address could not be parsed as an URI reference: ${ex.getMessage()}")
        }
        HttpPut putRequest = new HttpPut(uri)
        putRequest.addHeader("Authorization", bearerToken)

        // Form the Policy JSON for the REST call
        StringEntity policyEntity = new StringEntity(policy)
        policyEntity.setContentType("application/json")
        putRequest.setEntity(policyEntity)

        // Execute the Put REST Call
        HttpResponse response
        try {
            response = client.execute(putRequest)
        }
        catch (IOException ex) {
            throw new IOException ("[Error] The Put Request could not be called due to an error: ${ex.getMessage()}")
        }
        return response
    }

    /**
     * On success, REST server responds often return information about the given resource. They are
     * returned as a JSON entity. This function will retrieve the response entity and convert
     * the JSON in a data structure of lists and maps.
     *
     * @param response  The HTTP response message the server responded with
     * @return          The returned enitity in an interpretable data structure of lists and maps
     */
    public def httpResponse2Map(HttpResponse response){
        // Parse the returned JSON
        def json = response.entity?.content?.text
        JsonSlurper slurper = new JsonSlurper()

        def map
        try {
            map = slurper.parseText(json)
        }
        catch (JsonException ex) {
            throw new JsonException ("[Error] The response Json could not be parsed: ${ex.getMessage()}")
        }
        return map
    }
}