/*
 * Licensed Materials - Property of HCL
 * UrbanCode Deploy
 * (c) Copyright HCL Technologies Ltd. 2019. All Rights Reserved.
 */
package com.urbancode.urelease.integration.xldeploy.http

import com.urbancode.commons.web.util.PercentCodec
import com.urbancode.urelease.integration.xldeploy.config.XLConfigType
import com.urbancode.urelease.integration.xldeploy.config.XLServerState

import org.apache.log4j.Logger
import org.codehaus.jettison.json.JSONArray
import org.codehaus.jettison.json.JSONObject

class XLRestClient {
    final String QUERY_URI = "/deployit/repository/v2/query"
    final String TASK_QUERY_URI = "/deployit/tasks/v2/query"
    final String STATE_URI = "/deployit/server/state"
    final String CONFIG_ITEM_URI = "/deployit/repository/ci/%s"
    final String PREPARE_DEPLOY_URI = "/deployit/deployment/prepare/initial"
    final String MAP_DEPLOY_URI = "/deployit/deployment/previewblock"
    final String PREPARE_DEPLOYEDS_URI = "/deployit/deployment/prepare/deployeds"
    final String VALIDATE_DEPLOY_URI = "/deployit/deployment/validate"
    final String EXECUTE_DEPLOY_URI = "/deployit/deployment"
    final String START_TASK_URI = "/deployit/tasks/v2/%s/start"
    final String TASK_URI = "/deployit/tasks/v2/%s"

    private RestClientHelper restHelper
    private Logger logger

    public XLRestClient(String url, String user, String pass) {
        restHelper = new RestClientHelper(url, user, pass, true)
        restHelper.setRequestHeaders(["Accept":"application/json",
            "Content-Type":"application/json"])

        logger = Logger.getLogger(getClass())
    }

    /**
     * Request a specific type of configuration object from the XL Deploy repository.
     * @param type
     * @return The JSONArray of configuration objects.
     */
    public JSONArray getConfigObjects(
        XLConfigType type,
        String modifiedAfter,
        int page,
        int resultsPerPage)
    {
        String URI = "${QUERY_URI}?type=${type}&page=${page}&resultsPerPage=${resultsPerPage}"

        if (modifiedAfter != null && !modifiedAfter.isEmpty()) {
            URI += "&lastModifiedAfter=${encodePath(modifiedAfter)}"
        }

        return new JSONArray(restHelper.doGetRequest(URI))
    }

    public JSONObject getConfigObject(String ref) {
        String URI = String.format(CONFIG_ITEM_URI, encodeRef(ref))

        return new JSONObject(restHelper.doGetRequest(URI))
    }

    public JSONArray getTasks(String beginDate, int page, int resultsPerPage) {
        String URI = "${TASK_QUERY_URI}?page=${page}&resultsPerPage=${resultsPerPage}"

        if (beginDate != null && !beginDate.isEmpty()) {
            URI += "&begindate=${encodePath(beginDate)}"
        }

        return new JSONArray(restHelper.doGetRequest(URI))
    }

    public boolean checkConnection() {
        String serverState = getServerState()

        if (XLServerState.MAINTENANCE.equalsIgnoreCase(serverState)) {
            logger.warn("The server is in maintenance mode. You will not be able to deploy.")
        }

        return true
    }

    public String getServerState() {
        JSONObject stateObj = new JSONObject(restHelper.doGetRequest(STATE_URI))
        return stateObj.getString("current-mode")
    }

    public String getTaskState(String taskId) {
        String taskURI = String.format(TASK_URI, taskId)
        JSONObject taskObject = new JSONObject(restHelper.doGetRequest(taskURI))

        return taskObject.getString("state")
    }

    public void startTask(String taskId) {
        String URI = String.format(START_TASK_URI, taskId)
        restHelper.doPostRequest(URI)
    }

    public String createDeployTask(String envRef, String versionRef) {
        JSONObject deployment = initDeployment(envRef, versionRef)
        mapDeployment(deployment)
        JSONObject preparedDeployment = prepareDeployeds(deployment)
        JSONObject validDeployment = validateDeployment(preparedDeployment)

        JSONObject deployTask = new JSONObject(restHelper.doPostRequest(EXECUTE_DEPLOY_URI,
            validDeployment.toString()))

        /* Return the ID of the deployment task */
        return deployTask.getString("string")
    }

    private JSONObject initDeployment(String envRef, String versionRef) {
        String URI = "${PREPARE_DEPLOY_URI}" +"?environment=${encodePath(envRef)}" +
            "&version=${encodePath(versionRef)}"

        return new JSONObject(restHelper.doGetRequest(URI))
    }

    private void mapDeployment(JSONObject deployment) {
        restHelper.doPostRequest(MAP_DEPLOY_URI, deployment.toString())
    }

    private JSONObject prepareDeployeds(JSONObject deployment) {
        JSONObject preparedDeployment =
            new JSONObject(restHelper.doPostRequest(PREPARE_DEPLOYEDS_URI, deployment.toString()))

        return preparedDeployment
    }

    private JSONObject validateDeployment(JSONObject deployment) {
        JSONObject validDeployment = new JSONObject(restHelper.doPostRequest(VALIDATE_DEPLOY_URI,
            deployment.toString()))

        return validDeployment
    }

    /**
     * Encode HTML form parameters.
     * @param path
     * @return The encoded parameter.
     * @throws UnsupportedEncodingException
     */
    private String encodePath(String path) throws UnsupportedEncodingException {
        return URLEncoder.encode(path, "UTF-8");
    }

    /**
     * Encode an object reference path to be used in a URL.
     * @param path
     * @return The encoded parameter.
     * @throws UnsupportedEncodingException
     */
    private String encodeRef(String ref) {
        PercentCodec encoder = new PercentCodec()
        List<String> segments = ref.split('/')

        for (int i = 0; i < segments.size(); i++) {
            segments.set(i, encoder.encode(segments[i]))
        }

        return segments.join('/')
    }
}
