package integration

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestRunIntegrationTestsScriptExecutesSuiteCommand(t *testing.T) {
	bashPath, err := exec.LookPath("bash")
	if err != nil {
		t.Skip("bash not available")
	}

	repoRoot := repoRootFromTestFile(t)
	suitesRoot := filepath.Join(repoRoot, "integration", "suites")
	suiteDir, err := os.MkdirTemp(suitesRoot, "runner-script-")
	if err != nil {
		t.Fatalf("MkdirTemp() error = %v", err)
	}
	t.Cleanup(func() {
		_ = os.RemoveAll(suiteDir)
	})

	suiteName := filepath.Base(suiteDir)
	err = os.WriteFile(
		filepath.Join(suiteDir, "suite.env"),
		[]byte("TEST_COMMAND='printf runner-ok'\n"),
		0o644,
	)
	if err != nil {
		t.Fatalf("WriteFile(suite.env) error = %v", err)
	}
	err = os.WriteFile(
		filepath.Join(suiteDir, "docker-compose.yml"),
		[]byte("services:\n  fake-dependency:\n    image: busybox\n"),
		0o644,
	)
	if err != nil {
		t.Fatalf("WriteFile(docker-compose.yml) error = %v", err)
	}

	stubDir := t.TempDir()
	logPath := filepath.Join(t.TempDir(), "docker.log")
	err = os.WriteFile(filepath.Join(stubDir, "docker"), []byte(`#!/bin/sh
set -eu

log_file="${DOCKER_LOG:?}"
{
  printf '%s\n' '---'
  for arg in "$@"; do
    printf '%s\n' "$arg"
  done
} >>"$log_file"

subcommand=""
for arg in "$@"; do
  case "$arg" in
    config|up|run|down)
      subcommand="$arg"
      ;;
  esac
done

case "$subcommand" in
  config)
    printf '%s\n' integration-runner fake-dependency
    ;;
  up)
    ;;
  run)
    printf '%s\n' runner-ok
    ;;
  down)
    ;;
  *)
    printf 'unexpected docker invocation: %s\n' "$*" >&2
    exit 1
    ;;
esac
`), 0o755)
	if err != nil {
		t.Fatalf("WriteFile(docker stub) error = %v", err)
	}

	cmd := exec.Command(bashPath, filepath.Join(repoRoot, "scripts", "run-integration-tests.sh"), suiteName)
	cmd.Dir = repoRoot
	cmd.Env = append(os.Environ(),
		"PATH="+stubDir+string(os.PathListSeparator)+os.Getenv("PATH"),
		"DOCKER_LOG="+logPath,
	)

	output, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("run-integration-tests.sh error = %v\noutput:\n%s", err, output)
	}
	if !strings.Contains(string(output), "runner-ok") {
		t.Fatalf("script output did not include runner output:\n%s", output)
	}

	logData, err := os.ReadFile(logPath)
	if err != nil {
		t.Fatalf("ReadFile(logPath) error = %v", err)
	}

	runArgs := findLoggedDockerInvocation(t, string(logData), "run")
	if strings.Contains(strings.Join(runArgs, "\n"), "\nsh\n-c\n") {
		t.Fatalf("docker compose run unexpectedly wrapped TEST_COMMAND with sh -c:\n%v", runArgs)
	}

	if !containsArg(runArgs, "integration-runner") {
		t.Fatalf("docker compose run args missing runner service:\n%v", runArgs)
	}
	if !containsArg(runArgs, "printf runner-ok") {
		t.Fatalf("docker compose run args missing suite command as a single argument:\n%v", runArgs)
	}
}

func repoRootFromTestFile(t *testing.T) string {
	t.Helper()

	wd, err := os.Getwd()
	if err != nil {
		t.Fatalf("Getwd() error = %v", err)
	}
	return filepath.Dir(wd)
}

func findLoggedDockerInvocation(t *testing.T, logData, subcommand string) []string {
	t.Helper()

	for _, block := range strings.Split(logData, "---\n") {
		block = strings.TrimSpace(block)
		if block == "" {
			continue
		}
		args := strings.Split(block, "\n")
		if containsArg(args, subcommand) {
			return args
		}
	}

	t.Fatalf("did not find docker %q invocation in log:\n%s", subcommand, logData)
	return nil
}

func containsArg(args []string, want string) bool {
	for _, arg := range args {
		if arg == want {
			return true
		}
	}
	return false
}
