// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT
package soak

import (
	"fmt"
	"log"
	"runtime"
	"strings"
	"testing"

	"github.com/aws/amazon-cloudwatch-agent-test/environment"
	"github.com/aws/amazon-cloudwatch-agent-test/util/common"
	"github.com/shirou/gopsutil/v3/process"
	"github.com/stretchr/testify/require"
)

func init() {
	environment.RegisterEnvironmentMetaDataFlags()
}

type testConfig struct {
	logFileCount      int
	linesPerSecond    int
	lineSizeBytes     int
	emfFileCount      int
	eventsPerSecond   int
	statsdClientCount int
	tps               int
	metricCount       int
}

func TestSoakHigh(t *testing.T) {
	tc := testConfig{
		logFileCount:      10,
		linesPerSecond:    4000,
		lineSizeBytes:     120,
		emfFileCount:      2,
		eventsPerSecond:   1600,
		statsdClientCount: 5,
		tps:               100,
		metricCount:       100,
	}
	switch runtime.GOOS {
	case "darwin":
		runTest(t, "resources/soak_high_darwin.json", tc)
	case "linux":
		runTest(t, "resources/soak_high_linux.json", tc)
	case "windows":
		runTest(t, "resources/soak_high_windows.json", tc)
	}
}

func runTest(t *testing.T, configPath string, tc testConfig) {
	common.CopyFile(configPath, common.ConfigOutputPath)
	require.NoError(t, common.StartAgent(common.ConfigOutputPath, false, false))
	require.NoError(t, startLogGen(tc.logFileCount, tc.linesPerSecond, tc.lineSizeBytes))
	require.NoError(t, startEMFGen(tc.emfFileCount, tc.eventsPerSecond))
	require.NoError(t, startStatsd(tc.statsdClientCount, tc.tps, tc.metricCount))
}

// startLogGen starts a long running process that writes lines to log files.
func startLogGen(fileNum int, eventsPerSecond int, eventSize int) error {
	err := killExisting("log-generator")
	if err != nil {
		return err
	}

	// Assume PWD is the .../test/soak/ directory.
	cmd := fmt.Sprintf("go run ../../cmd/log-generator -fileNum=%d -eventsPerSecond=%d -eventSize=%d",
		fileNum, eventsPerSecond, eventSize)
	return common.RunAsyncCommand(cmd)
}

// startEMFGen starts a long running process that writes EMF events to log files.
func startEMFGen(fileNum int, eventsPerSecond int) error {
	err := killExisting("emf-generator")
	if err != nil {
		return err
	}
	cmd := fmt.Sprintf("go run ../../cmd/emf-generator -fileNum=%d -eventsPerSecond=%d",
		fileNum, eventsPerSecond)
	return common.RunAsyncCommand(cmd)
}

// startStatsd starts a long running process that sends statsd metrics to a port.
func startStatsd(clientNum int, tps int, metricNum int) error {
	err := killExisting("statsd-generator")
	if err != nil {
		return err
	}
	cmd := fmt.Sprintf("go run ../../cmd/statsd-generator -clientNum=%d -tps=%d -metricNum=%d",
		clientNum, tps, metricNum)
	return common.RunAsyncCommand(cmd)
}

// killExisting will search the command line of every process and kill any that match.
// Return nil if no matches found, or if all matches killed successfuly.
func killExisting(name string) error {
	procs, err := process.Processes()
	if err != nil {
		return err
	}
	for _, p := range procs {
		c, _ := p.Cmdline()
		if strings.Contains(c, name) {
			n, _ := p.Name()
			log.Printf("killing process, name %s, cmdline %s", n, c)
			err = p.Kill()
			if err != nil {
				return err
			}
		}
	}
	return nil
}