/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* IBM UrbanCode Deploy
* IBM UrbanCode Release
* IBM AnthillPro
* (c) Copyright IBM Corporation 2002, 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.plugin.odm

import groovy.json.JsonBuilder
import groovy.json.JsonException

import org.apache.http.client.HttpClient
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType
import org.apache.http.entity.FileEntity
import org.apache.http.HttpResponse
import org.apache.http.HttpStatus
import org.xml.sax.SAXException

import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder

public class OdmHelper {

    private URL url
    private HttpClient client

    /**
    *  A class to assist in executing ODM operations via REST API
    *  @param username The username to authenicate with the Rule Execution Server
    *  @param password The password to authenicate with the Rule Execution Server
    */
    public OdmHelper(String username, String password) {
        HttpClientBuilder builder = new HttpClientBuilder()
        builder.setTrustAllCerts(true)
        builder.setPreemptiveAuthentication(true)
        builder.setUsername(username)
        builder.setPassword(password)
        client = builder.buildClient()
    }

    /**
    *  Set the URL endpoint of the REST call to execute
    *  @param hostname The hostname of the Rule Exectuion Server
    *  @param port The port that the Rule Execution Server is listening on
    *  @param path The path to the specific REST endpoint
    *  @return True if the URL is valid, false otherwise
    */
    public boolean setUrl(String hostname, String port, String path) {
        if (!hostname.startsWith('http') && !hostname.startsWith('https')) {
            hostname = 'http://' + hostname
        }
        try {
            url = new URL("${hostname}:${port}/${path}")
        }
        catch (MalformedURLException e) {
            println ("[error]  Invalid URL: ${e.getMessage()}")
            return false
        }
        return true
    }

    /**
    *  Deploy a XOM Resource (Jar or Zip) to a Rule Execution Server
    *  @param xFile The path to the XOM Resource file to deploy
    *  @return True if the XOM resource is sucessfully deployed to the Rule Execution Server, false otherwise
    */
    public boolean deployXom(String xFile) {
        File file = new File(xFile)
        if (!file.isFile()) {
            println ("[error]  ${filename} is not a file.")
            println ('[possible solution]  Please update the step configuration with valid file for the XOM File property.')
            return false
        }
        def posted = post(file)
        if (!posted) {
            println ("[error]  Could not deploy XOM resource - ${posted}")
            return false
        }
        return getStatus(posted)
    }

    /**
    *  Execute an HTTP POST to the class URL
    *  @param file The file to send as an application/octet-stream
    *  @return The text of the body contained in the HTTP Response or false for any failure
    */
    private def post(File file) {
        println ("[action]  Executing POST to ${url.toString()}")
        HttpPost request = new HttpPost(url.toString())
        try {
            request.setEntity(new FileEntity(file, ContentType.APPLICATION_OCTET_STREAM))
        }
        catch (IllegalArgumentException e) {
            println ("[error]  Could not set POST entity: ${e.getMessage()}.")
            return false
        }
        HttpResponse response
        try {
            response = client.execute(request)
        }
        catch (IOException ioe) {
            println ("[error]  Problem executing HTTP request: ${ioe.getMessage()}.")
            println ("Response: ${response}")
            return false
        }
        if (!response.getStatusLine().getStatusCode().equals(HttpStatus.SC_CREATED)) {
            println ("[error]  Bad response code of ${response.getStatusLine().getStatusCode()} - ${response}.")
            println ('Response:\n' + response.entity?.content?.text)
            return false
        }
        return response.entity?.content?.text
    }

    /**
    *  Retrieve whether or not the deployment was successful from the HTTP Response
    *  @param response The HTTP Response XML to check success status of
    *  @return True if the deployment was successful, false otherwise
    */
    private def getStatus(def response) {
        def parser
        try {
            parser = new XmlParser().parseText(response)
            return parser.succeeded
        }
        catch (IOException e) {
            println ('[error]  Could not process response.')
            println ("Response: ${parser.toString().replace('\n', '')}")
            return false
        }
        catch (SAXException e) {
            println ('[error]  XML is not well-formed.')
            println ("XML: ${parser.toString().replace('\n', '')}")
            return false
        }
        catch (MissingPropertyException e) {
            println ('[error]  Could not find status in response.')
            println ("Response: ${parser.toString().replace('\n', '')}")
            return false
        }
        catch (NullPointerException e) {
            println ('[error]  Could not get status from response.')
            println ("Response: ${parser.toString().replace('\n', '')}")
            return false
        }
    }
}
