/*
* 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.docker

import groovy.io.FileType
import groovy.json.JsonException
import groovy.json.JsonSlurper

import java.io.IOException;
import java.net.URI
import java.util.regex.Matcher
import java.util.regex.Pattern

public class DockerUtils {

    /**
    *  @param url The string to attempt to convert to a URI
    *  @return The URI representation of the url, false if any syntax errors
    */
    public static def stringToUri(String url) {
        try {
            return new URI(url)
        }
        catch (URISyntaxException use) {
            println ("[error]  Invalid syntax for URL: ${use.getMessage()}.")
            println ('[possible solution]  Please update the step configuration with a vaild URL.')
            return new URI('')
        }
    }

    /**
    *  @param list The list to trim whitespaces and remove null entries
    *  @param delimiter The string that separates each entry
    */
    public static def toTrimmedList(def list, String delimiter) {
        return list.split(delimiter).findAll{ it?.trim() }.collect{ it.trim() }
    }

    /**
    * @param input String list of elements containing key value pairs
    * @param delimiter Characters used to delimit the key value pair
    * @return Map<String, String> containing the key value pairs
    */
    public static Map<String, String> toKeyValueMap(List<String> input, String delimiter) {
        Map<String, String> result = new HashMap<String,String>()
        input.each{ it ->
            String[] parts = it.split(delimiter, 2)*.trim()
            def propName = parts[0]
            def propValue = parts.size() == 2 ? parts[1] : ''
            result.put(propName, propValue)
        }
        return result
    }

    /**
    *  @param input The string to check for environment references in
    *  @return A set of keys referencing environments
    */
    public static def getEnvReferences(String input) {
        def result = [] as Set
        if (input != null && input.trim().length() > 0) {
            Pattern pattern = Pattern.compile('\\$\\{[^}]*}')
            Matcher matcher = pattern.matcher(input)
            while (matcher.find()) {
                String key = input.substring(matcher.start() + 2, matcher.end() - 1)
                result << key
            }
        }
        return result
    }

    /**
    *  @param rawImageSpec The raw, unformatted image spec
    *  @return The specs image, registry and version of the image spec
    */
    public static def parseImageSpec(String rawImageSpec) {
        def result = new Expando()

        // qualified domain         registry.tld/org/namespace/image:1.0
        // -------- or --------     registry.tld/org/namespace/image
        // simple host and port     localhost:5000/namespace/image:1.0
        // -------- or --------     localhost:5000/namespace/image
        // implicit library         registry.tld/namespace/image:1.0
        // ------   or --------     registry.tld/namespace/image
        // no/implicit registry     namespace/image:1.0
        // ------   or --------     namesapce/image
        // implicit local library   image:1.0
        // ------   or --------     image
        // colon in repo spec       my:image:1.0
        // ------   or --------     my:image
        // Repo is everything before first slash, unless spec contains no dots and no colon before first slash.
        // If registry spec exists, remove it and trailing slash - called Repository Spec
        def registryImageArray = rawImageSpec.split("/", 2)

        //Break this string into a repo+tag and registry
        def repoAndTag
        def registrySpec
        def firstPart = registryImageArray[0]
        if (registryImageArray.length == 1 ) {
            //No slash - implicit registrySpec
            repoAndTag = firstPart
        }
        else if ( firstPart.contains(".") || firstPart.contains(":") ) {
            registrySpec = firstPart
            repoAndTag = registryImageArray[1]
        }
        else {
            repoAndTag = rawImageSpec
        }

        def imageSpec
        def versionSpec

        if (repoAndTag.contains(":")) {
            //is explicit version/tag
            def imageRefParts = repoAndTag.split(":")
            imageSpec = imageRefParts[0]
            versionSpec = imageRefParts[1]

        }
        else {
            //is implicit version
            imageSpec = repoAndTag
        }

        result.registry = registrySpec
        result.image = imageSpec
        result.version = versionSpec

        return result
    }

    /**
    *  @param registry The docker registry containing the image
    *  @param imageName The name of the image to build the spec for
    *  @param tag The tag of the image to build the spec for
    *  @return The image specification of name:tag
    */
    public static String buildImageSpec(String registry, String imageName, String tag) {
        if (imageName) {
            def result = ""
            if (registry) {
                result = "$registry/"
            }

            result += imageName

            if (tag) {
                result += ":" + tag
            }

            return result.toString()
        }
    }

    /**
    *  @param jsonText The text to turn into a Json object
    *  @return An object representation of a Json structure
    */
    public static def parseTextAsJson(def jsonText) {
        def parsedText
        def text = jsonText.toString()
        if (text) {
            try {
                parsedText = new JsonSlurper().parseText(text)
            }
            catch (JsonException jse) {
                println ("[error]  Could not parse text as JSON: ${text.replace('\n', '')}")
            }
        }
        return parsedText
    }

    /**
    *  @param input The file or directory of the docker-compose executable
    *  @return Canonical path String to the docker-compose executable
    */
    public static String findDockerComposeExecutable(String input) {
        final String EXECUTABLE = "docker-compose"
        String result = EXECUTABLE
        // If input is not the default EXECUTABLE value, continue
        if (input != EXECUTABLE) {
            File exe = new File(input)

            // If input is directory, look for the EXECUTABLE
            if (exe.isDirectory()) {
                boolean dcFound = false
                // Loop through all files looking for EXECUTABLE
                exe.eachFile(FileType.FILES) { file ->
                    if (file.getName().contains(EXECUTABLE)) {
                        dcFound = true
                    }
                }

                // Fail if EXECUTABLE is not found in directory
                if (!dcFound) {
                    throw new IOException("[Error] The specified folder '${input}' " +
                        "does not contain the ${EXECUTABLE} executable.")
                }

                // Construct path to executable
                result = exe.getCanonicalPath() + File.separator + EXECUTABLE

            // If input is file, confirm it's the EXECUTABLE
            } else if (exe.isFile()) {

                // If EXECUTABLE name does not exist in input file name, fail
                if (!exe.getName().contains(EXECUTABLE)) {
                    throw new FileNotFoundException("[Error] The specified file '${input}' " +
                        "is not a ${EXECUTABLE} executable.")
                }
                result = exe.getCanonicalPath()
            }

            // Fail if input is neither a file nor directory
            else {
                throw new IOException("[Error] The specified ${EXECUTABLE} input '${input}' does not exist.")
            }
        }

        // Escape spaces in the command path
        if (result.contains(" ")) {
            result = "\"" + result + "\""
        }

        println "[Ok] Using ${EXECUTABLE} executable: ${result}"
        return result
    }

}
