/*
 * Licensed Materials - Property of IBM* and/or HCL**
 * UrbanCode Deploy
 * (c) Copyright IBM Corporation 2013, 2017. All Rights Reserved.
 * (c) Copyright HCL Technologies Ltd. 2018, 2019. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 *
 * * Trademark of International Business Machines
 * ** Trademark of HCL Technologies Limited
 */
package com.urbancode.air.plugin.onetest

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

import org.apache.http.client.entity.UrlEncodedFormEntity
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.Header
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.LaxRedirectStrategy
import org.apache.http.NameValuePair
import org.apache.http.message.BasicNameValuePair
import org.apache.http.util.EntityUtils

import com.urbancode.air.XTrustProvider
import com.urbancode.commons.httpcomponentsutil.HttpClientBuilder;

public class OneTestRestHelper {
    def airPluginTool;
    def props = [];

    URL otsUrl;
    String serverUrl;
    String offlineToken;
    String filepath;
    String repoId;
    String projId;
    String assetName;
    String type;
    String branchName;
    String startDate;
    String datasetsString;
    String variablesString;
    String tagsString;
    String[] datasetsList
    String[] variablesList
    String[] tagsList
    def datasets;
    def variables;
    def tags;
    String secretsCollectionName;
    String ritEnv;
    String proxyHost;
    String proxyPass;
    String proxyPort;
    String proxyUser;
    String allowInsecure;

    CloseableHttpClient client;
    HttpClientBuilder clientBuilder;

    public OneTestRestHelper(def airPluginToolIn) {
        airPluginTool = airPluginToolIn;
        props = airPluginTool.getStepProperties();

        offlineToken = props['offlineToken'].trim();
        filepath = props['filepath']?.trim();
        repoId = props['repoId']?.trim();
        projId = props['projId']?.trim();
        type = props['type']?.trim();
        branchName = props['branchName']?.trim();
        startDate = props['startDate']?.trim();
        datasetsString = props['datasets']
        variablesString = props['variables']
        tagsString = props['tags']
        secretsCollectionName = props['secretsCollectionName']?.trim();
        ritEnv = props['ritEnv']?.trim();
        proxyHost = props['proxyHost']?.trim();
        allowInsecure = Boolean.valueOf(props['allowInsecure']);

        //Get assetName from filename
        assetName = filepath.tokenize('/').last()
        assetName = assetName.take(assetName.lastIndexOf('.'))

        datasets = []
        if (datasetsString) {
            datasetsList = datasetsString.split("\n")*.trim()
            for (dataset in datasetsList) {
                datasets.add('{"internalResourceId":"' + dataset.tokenize(",")[0] +
                        '", "override":"' + dataset.tokenize(",")[1] +
                        '", "source":"' + dataset.tokenize(",")[2] +
                        '"}')
            }
        }

        variables = []
        if (variablesString) {
            variablesList = variablesString?.split('\n')*.trim()
            for (variable in variablesList) {
                variables.add('{"name":"' + variable.tokenize(",")[0] + '", "value":"' + variable.tokenize(",")[1] + '"}')
            }
        }

        tags = []
        if (tagsString) {
            tagsList = tagsString?.split(',')*.trim()
            tags = tagsList.collect { '{"tag":"' + it + '"}' };
        }

        if (!secretsCollectionName) {
            secretsCollectionName = null
        }


        try {
            otsUrl = new URL(props['otsUrl'].trim());
        } catch (MalformedURLException e) {
            println e.getMessage();
            System.exit(1);
        }

        serverUrl = 'https://' + otsUrl.getAuthority()


        println "[Info] Using serverUrl: '${serverUrl}'";

        clientBuilder = new HttpClientBuilder();
        clientBuilder.setDisableCookies(true);
        clientBuilder.setPreemptiveAuthentication(false);
        clientBuilder.setRedirectStrategy(new LaxRedirectStrategy())


        if (allowInsecure) {
           XTrustProvider.install()
           clientBuilder.setTrustAllCerts(true)
        }

        if (proxyHost) {
            proxyPass = props['proxyPass'];
            proxyPort = props['proxyPort'].trim();
            proxyUser = props['proxyUser'].trim();

            println "[Info] Using Proxy Host ${proxyHost}";
            println "[Info] Using Proxy Port ${proxyPort}";

            clientBuilder.setProxyHost(proxyHost);
            clientBuilder.setProxyPassword(proxyPass);
            clientBuilder.setProxyPort(Integer.valueOf(proxyPort));
            clientBuilder.setProxyUsername(proxyUser);
            if(proxyUser) {
                println "[Info] Using Proxy User ${proxyUser}";
            }
        }

        client = clientBuilder.buildClient();
    }

    // Get the active session token from the offline refresh token
    def getAccessToken() {
        def parsedJson;

        HttpPost getTokenMethod = new HttpPost(serverUrl + '/rest/tokens/')
        getTokenMethod.addHeader("Accept", "application/json");
        getTokenMethod.addHeader("Content-Type", "application/x-www-form-urlencoded");
        ArrayList<NameValuePair> postParameters = new ArrayList<NameValuePair>();
        postParameters.add(new BasicNameValuePair("refresh_token", "${offlineToken}"));

        getTokenMethod.setEntity(new UrlEncodedFormEntity(postParameters, "UTF-8"));

        def resp = client.execute(getTokenMethod);

        println (resp.getStatusLine().toString());

        def statusCode = resp.getStatusLine().getStatusCode();
        if (statusCode < 200 || statusCode >= 300) {
            println "[Error] Request failed with status ${statusCode}. Exiting Failure.";
            println ('Response:\n' + resp.entity?.content?.getText("UTF-8"));
            System.exit(1);
        }
        def entity = EntityUtils.toString(resp.getEntity());

        def slurper = new JsonSlurper();
        try {
            parsedJson = slurper.parseText(entity);
        }
        catch (groovy.json.JsonException e) {
            println "Failed to parse response body. Printing useful debugging information and exiting.";
            println "Response status code: ${statusCode}.";
            println "Header:";
            Header[] headers = resp.getAllHeaders();
            for (Header header : headers) {
                System.out.println(header.getName() + ":"+ header.getValue());
            }
            println "Body:";
            println entity;
            println "Stacktrace:";
            e.printStackTrace();
            System.exit(1);
        }
        return parsedJson
    }

    // Kick off a OneTest Job
    def startJob() {
        def accessJson = getAccessToken();
        String accessToken = accessJson.access_token;
        String refreshToken = accessJson.refresh_token;

        HttpPost startJobMethod = new HttpPost(serverUrl + '/rest/projects/' + projId + '/jobs/');
        startJobMethod.addHeader("Accept", "application/json");
        startJobMethod.addHeader("Content-Type", "application/json");
        startJobMethod.addHeader("Authorization", "Bearer " + accessToken);
        String methodBody =
                '{"filepath": "' + filepath + '",' +
                '"repoid": "' + repoId + '",' +
                '"projId": "' + projId + '",' +
                '"assetName": "' + assetName + '",' +
                '"type": "' + type + '",' +
                '"branchName": "' + branchName + '",' +
                '"startDate": "' + startDate + '",' +
                '"datasets": ' + datasets.toString() + ',' +
                '"tags": ' + tags.toString() + ',' +
                '"offlineToken": "' + refreshToken + '",' +
                '"variables": ' + variables.toString() + ',' +
                '"ritEnv": "' + ritEnv + '"}'
        StringEntity postEntity = new StringEntity(methodBody);
        startJobMethod.setEntity(postEntity);

        def resp = client.execute(startJobMethod);

        println("Response: " + resp.getStatusLine().toString())

        def statusCode = resp.getStatusLine().getStatusCode();
        if (statusCode != 201) {
            println "[Error] Request failed with status ${statusCode}. Exiting Failure.";
            println('Response:\n' + resp.entity ?.content ?.getText("UTF-8"));
            System.exit(1);
        }
    }
}
