/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Deploy
 * (c) Copyright IBM Corporation 2014, 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.artifactory

import groovy.json.JsonException
import groovy.json.JsonSlurper

import java.security.NoSuchAlgorithmException

import org.apache.commons.lang.ObjectUtils
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity
import org.apache.http.HttpResponse
import org.apache.http.HttpStatus
import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.util.EntityUtils


import com.urbancode.commons.fileutils.digest.DigestUtil
import com.urbancode.commons.fileutils.FileUtils
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder


public class ArtifactoryHelper {
    private static final HttpClientBuilder builder = new HttpClientBuilder();
    private static DefaultHttpClient client;
    private static final ARTIFACT_FILE_HASH_ALGORITHM = "sha1";

    public static HttpClientBuilder getBuilder() {
        return builder;
    }

    public static void createClient() {
        client = builder.buildClient();
    }

    public static void createClient(username, password) {
        builder.setPreemptiveAuthentication(true);
        builder.setUsername(username);
        builder.setPassword(password);
        client = builder.buildClient();
    }

    public static DefaultHttpClient getClient() {
        return client;
    }

    public static void verifyHash (File fileToVerify, storedDigest) {
        if (storedDigest != null) {
            String computedDigest;
            try {
                computedDigest = DigestUtil.getHexDigest(fileToVerify, ARTIFACT_FILE_HASH_ALGORITHM);
                if (!ObjectUtils.equals(storedDigest, computedDigest)) {
                    throw new Exception("[error]  Artifact file verification of ${fileToVerify.getName()} failed. Expected "
                    + " digest of ${storedDigest} but the downloaded file was ${computedDigest}");
                }
            }
            catch (NoSuchAlgorithmException e) {
                throw new Exception("[error]  Algorithm to verify Maven remote artifacts not supported: " +
                ARTIFACT_FILE_HASH_ALGORITHM);
            }
            catch (IOException e) {
                throw new Exception("[error]  Error verifying downloaded Maven remote artifacts: ${e.getMessage()}", e);
            }
        }
    }


    public static File downloadFileFromRepo(String url, String checkHash) {
        def response = executeHttpRequest(url, null)
        if (!response) {
            throw new Exception("[error]  Could not find file ${url} in artifactory: ${response}");
        }
        int status = response.getStatusLine().getStatusCode();
        if (status == HttpStatus.SC_OK) {
            def jsonString = EntityUtils.toString(response.getEntity());
            def slurper = new JsonSlurper();
            def infoJSON = slurper.parseText(jsonString);
            def checksumMap = infoJSON.checksums;
            def downloadUrl = infoJSON.downloadUri;
            System.out.println("[action]  Downloading: ${downloadUrl}");
            response = executeHttpRequest(downloadUrl, null)
            if (!response) {
                throw new Exception("[error]  Could not download file : ${downloadUrl}\nResponse : ${response}");
            }
            status = response.getStatusLine().getStatusCode();
            if (status == HttpStatus.SC_OK) {
                String tempFileSuffix = ".maven2";
                int extIndex = url.lastIndexOf(".");
                if (extIndex >= 0) {
                    tempFileSuffix = url.substring(extIndex);
                }
                File artifactFile = File.createTempFile("maven2-", tempFileSuffix);
                FileUtils.writeInputToFile(response.getEntity().getContent(), artifactFile);
                if (checkHash && Boolean.valueOf(checkHash)) {
                    //verify checksum
                    verifyHash(artifactFile, checksumMap.sha1);
                    System.out.println("[ok]  Verification for file : ${artifactFile} succeeded!");
                }
                return artifactFile;
            } else {
                throw new Exception("[error]  Exception downloading file : ${downloadUrl}\nErrorCode : ${status.toString()}");
            }
        }
    }

    /**
    *  @param base The base URL of the Artifactory Server
    *  @param path The path to append to the base
    *  @return The combined base and path URL
    */
    public static String getCombinedUrl(String base, String path) {
        if (base.endsWith('/')) {
            base = base.substring(0, base.length - 1)
        }
        if (path.startsWith('/')) {
            path = path.substring(1)
        }
        return base + '/' + path
    }

    /**
    *  @param url The url to send the POST request
    *  @param json The JSON payload to add to the request
    */
    public static def jsonPost(String url, String json) {
        StringEntity jsonEntity
        try  {
            jsonEntity = new StringEntity(json, ContentType.APPLICATION_JSON)
        }
        catch (IllegalArgumentException iae) {
            println ("[error]  Could not create StringEntity for POST: ${iae.getMessage()}")
            println ("POST data: ${json}.replace('\n', '')")
            return false
        }
        catch (UnsupportedEncodingException uee) {
            println ("[error]  Could not create StringEntity for POST: ${uee.getMessage()}")
            println ("POST data: ${json}.replace('\n', '')")
            return false
        }
        def response = executeHttpRequest(url, jsonEntity)
        if (!response) {
            println ("[error]  Could not execute POST: ${response}")
            return false
        }
        int statusCode = response.getStatusLine().getStatusCode()
        String responseText = response.entity?.content?.text
        printMessages(responseText, 'ERROR')
        printMessages(responseText, 'INFO')
        if (statusCode != HttpStatus.SC_OK) {
            println ("[error]  Bad response from server: ${statusCode} - ${responseText.replace('\n', '')}")
            return false
        }
        return responseText
    }

    /**
    *  @param url The url of the HTTP Request to make
    *  @param postInfo The information to set as an entity for an HTTP POST
    *  @return The response received from executing the request
    */
    private static def executeHttpRequest(String url, def postInfo) {
        def request
        try {
            if (postInfo) {
                request = new HttpPost(url)
                request.setEntity(postInfo)
            }
            else {
                request = new HttpGet(url)
            }
        }
        catch (IllegalArgumentException iae) {
            println ("[error]  Invalid URL: ${iae.getMessage()}.")
            println ('[possible solution]  Please update the step configuration with a valid URL.')
            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
        }
        return response
    }

    /**
    *  @param response The response to parse for messages
    *  @param level The level of messages to print out
    */
    private static void printMessages(String text, String level) {
        try {
            def messages = new JsonSlurper().parseText(text)."messages"
            if (messages instanceof ArrayList && messages.size() > 0) {
                messages.each { message ->
                    if (message.level.equals(level)) {
                        println ("[${level.toLowerCase()}]  ${message.message}")
                    }
                }
            }
        }
        catch (JsonException jse) {
            println ("[error]  Returned JSON is not well-formed: ${jse.getMessage()}")
            println ("JSON: ${text.replace('\n', '')}")
        }
    }
}
