package com.urbancode.build.plugin.ucd.rest

/*
 * Licensed Materials - Property of HCL
 * UrbanCode Build
 * (c) Copyright HCL Technologies Ltd. 2019. All Rights Reserved.
 */
import com.urbancode.air.ExitCodeException
import com.urbancode.ud.client.ApplicationClient
import com.urbancode.ud.client.SystemClient

import java.awt.image.ComponentSampleModel

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

class UCDRestHelper {
    private ApplicationClient appClient
    private SystemClient systemClient
    private Logger logger

    public UCDRestHelper(String url, String user, String password, String authToken) {
        /* Auth Token will take precedence over username and password */
        if (authToken) {
            user = "PasswordIsAuthToken"
            password = authToken
        }

        appClient = new ApplicationClient(new URI(url), user, password, true)
        systemClient = new SystemClient(new URI(url), user, password, true)
        logger = Logger.getLogger(getClass())
    }

    /**
     * Create a snapshot of an environment, including all component versions in its inventory.
     * @param environment
     * @param application
     * @param snapshot
     * @param description
     * @return The ID of the newly created snapshot
     * @throws JSONException
     */
    public String createEnvironmentSnapshot(
        String environment,
        String application,
        String snapshot,
        String description)
    {
        logger.info("Creating a snapshot of environment '${environment}' in application " +
            "'${application}' named '${snapshot}'.")
        UUID snapshotId = appClient.createSnapshotOfEnvironment(environment, application,
            snapshot, description)

        return snapshotId.toString()
    }

    /**
     * Request an application process with either a snapshot or component & version.
     * @param request An object containing all of the application process request fields.
     * @return The ID of the application process request.
     */
    public String deploy(AppProcessRequest request) {
        logger.info("Requesting application process with the following configuration '${request}'.")

        Map<String, List<String>> versions
        if (request.snapshot) {
            /* Empty versions map still required even if snapshot is being deployed */
            versions = new HashMap<String, List<String>>()
        }
        else if (request.versions) {
            versions = request.versions
        }
        else {
            throw new ExitCodeException("No snapshot or component version specified.")
        }

        UUID requestId = appClient.requestApplicationProcess(
            request.application,
            request.applicationProcess,
            request.description,
            request.environment,
            request.snapshot,
            request.onlyChanged,
            versions,
            request.properties)

        return requestId.toString()
    }

    /**
     * Wait for application process to complete.
     * @param processId
     */
    public void waitForProcess(String processId, long timeout) {
        boolean processFinished = false
        long waitedSeconds = (timeout == -1) ? -1 : 0 // Timeout of -1 waits forever
        String deploymentResult


        while (!processFinished && waitedSeconds <= timeout) {
            deploymentResult = appClient.getApplicationProcessStatus(processId)

            if (!deploymentResult.isEmpty()
                    && !deploymentResult.equalsIgnoreCase("NONE")
                    && !deploymentResult.equalsIgnoreCase("SCHEDULED FOR FUTURE")) {
                processFinished = true;

                if (deploymentResult.equalsIgnoreCase("FAULTED")
                    || deploymentResult.equalsIgnoreCase("FAILED TO START")
                    || deploymentResult.equalsIgnoreCase("CANCELED"))
                {
                    throw new ExitCodeException("Process failed with result ${deploymentResult}.")
                }
            }

            // Give application process more time to complete
            Thread.sleep(3000)
            waitedSeconds += 3
        }

        if (!processFinished) {
            logger.debug("Process deployment result: ${deploymentResult}.")
            throw new ExitCodeException("Process did not complete before ${timeout} second timeout.")
        }
    }

    /**
     * Replace old versions of components with new versions.
     * @param snapshot Name or ID of the snapshot.
     * @param application Name or ID of the application.
     * @param newVersions Mapping of component names to a list of new versions.
     * @throws JSONException
     */
    private void updateSnapshotVersions(
        String snapshot,
        String application,
        Map<String, String> newVersions)
    {
        JSONArray components = appClient.getSnapshotVersions(snapshot, application)

        /* Iterate through all existing components in the snapshot */
        for (int i = 0; i < components.length(); i++) {
            JSONObject component = components.getJSONObject(i)
            String componentName = component.getString("name")

            /* When a new version is specified, its component must be emptied */
            if (newVersions.containsKey(componentName)) {
                JSONArray oldVersions = component.getJSONArray("desiredVersions")

                /* Clear out all old versions of the component */
                for (int j = 0; j < oldVersions.length(); j++) {
                    JSONObject oldVersion = oldVersions.getJSONObject(j)
                    String versionName = oldVersion.getString("name")

                    logger.info("Removing old version '${versionName}' from component " +
                        "'${componentName}' in snapshot.")
                    appClient.removeVersionFromSnapshot(snapshot, application,
                        versionName, componentName)
                }

                /* Add the new versions to that component */
                for (String newVersion : newVersions.get(componentName)) {
                    logger.info("Adding new component version '${newVersion}' to" +
                        " component '${componentName} in snapshot.")
                    appClient.addVersionToSnapshot(snapshot, application, newVersion, componentName)
                }
            }
        }
    }
}
