// Copyright 2015-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

// Package integ contains utility functions to run ECS CLI commands and check their outputs.
package integ

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/aws/amazon-ecs-cli/ecs-cli/integ/stdout"
)

const (
	binPath = "../../../bin/local/ecs-cli.test" // TODO: use abs path or env var
)

// GetCommand returns a Cmd struct with the right binary path & arguments
func GetCommand(args []string) *exec.Cmd {
	cmdPath := binPath

	if runtime.GOOS == "windows" {
		cmdPath = cmdPath + ".exe"
	}

	fname, err := createTempCoverageFile()
	if err != nil {
		return exec.Command(cmdPath, args...)
	}
	args = append([]string{fmt.Sprintf("-test.coverprofile=%s", fname)}, args...)
	return exec.Command(cmdPath, args...)
}

// RunCmd runs a command with args and returns its Stdout.
func RunCmd(t *testing.T, args []string) (stdout.Stdout, error) {
	cmd := GetCommand(args)
	out, err := cmd.CombinedOutput()
	return stdout.Stdout(out), err
}

// createTempCoverageFile creates a coverage file for a CLI command under $TMPDIR.
func createTempCoverageFile() (string, error) {
	tmpfile, err := ioutil.TempFile("", "coverage-*.out")
	if err != nil {
		return "", err
	}

	err = tmpfile.Close()
	if err != nil {
		return "", err
	}
	return tmpfile.Name(), nil
}

// GetRowValues takes a row of stdout and returns a slice of strings split by arbirary whitespace
func GetRowValues(row string) []string {
	spaces := regexp.MustCompile(`\s+`)
	return strings.Split(spaces.ReplaceAllString(row, " "), " ")
}

// SuggestedResourceName returns a resource name matching the template "{CODEBUILD_BUILD_ID}-{identifier}".
// The CODEBUILD_BUILD_ID in the name can be used to safely delete a resource if it belongs to an old test build.
// The identifier can be used to give a human-friendly resource name.
func SuggestedResourceName(identifiers ...string) string {
	return fmt.Sprintf("%s-%s", getBuildId(), strings.Join(identifiers, "-"))
}

// getBuildId returns the CodeBuild ID compatible with CloudFormation.
func getBuildId() string {
	return strings.Replace(os.Getenv("CODEBUILD_BUILD_ID"), ":", "-", -1) // replace all occurrences
}

// RetryUntilTimeout retries function f every sleepInS seconds until the timeoutInS expires.
func RetryUntilTimeout(t *testing.T, f func(t *testing.T) bool, timeoutInS time.Duration, sleepInS time.Duration) bool {
	numRetries := int64(timeoutInS) / int64(sleepInS)
	var i int64
	for ; i < numRetries; i++ {
		if ok := f(t); ok {
			return true
		}
		t.Logf("Current timestamp=%v, sleeping for %v", time.Now(), sleepInS)
		time.Sleep(sleepInS)
	}
	return false
}
