package runtime

import (
	"bytes"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"

	"nest/src/catalog"
)

func TestBuildRunArgs(t *testing.T) {
	opts := &catalog.Container{
		Network:   "host",
		CapAdd:    []string{"NET_RAW", "NET_ADMIN"},
		ExtraArgs: []string{"--dns", "1.1.1.1"},
	}
	got := BuildRunArgs("docker.io/parrotsec/security", "/usr/bin/nmap", "nmap",
		"/home/u/nest/persistence", opts, true, []string{"-sV", "10.0.0.1"})
	want := []string{
		"run", "--rm", "-i", "-t",
		"--label", "io.parrot.nest.managed=true",
		"--label", "io.parrot.nest.tool=nmap",
		"-v", "/home/u/nest/persistence:/persistence",
		"--workdir", "/persistence",
		"--network", "host",
		"--cap-add", "NET_RAW",
		"--cap-add", "NET_ADMIN",
		"--dns", "1.1.1.1",
		"docker.io/parrotsec/security", "/usr/bin/nmap", "-sV", "10.0.0.1",
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("BuildRunArgs:\n got %q\nwant %q", got, want)
	}
}

func TestBuildRunArgsMinimal(t *testing.T) {
	got := BuildRunArgs("img", "/usr/bin/x", "x", "/p", nil, false, nil)
	want := []string{
		"run", "--rm", "-i",
		"--label", "io.parrot.nest.managed=true",
		"--label", "io.parrot.nest.tool=x",
		"-v", "/p:/persistence",
		"--workdir", "/persistence",
		"img", "/usr/bin/x",
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("BuildRunArgs:\n got %q\nwant %q", got, want)
	}
}

// fakeRuntime writes an executable shell script emulating a container engine
// and prepends its directory to PATH (AGENTS.md §7).
func fakeRuntime(t *testing.T, name, script string) {
	t.Helper()
	dir := t.TempDir()
	path := filepath.Join(dir, name)
	if err := os.WriteFile(path, []byte(script), 0o755); err != nil {
		t.Fatal(err)
	}
	t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
}

func TestAutoPrefersPodman(t *testing.T) {
	fakeRuntime(t, "podman", "#!/bin/sh\nexit 0\n")
	fakeRuntime(t, "docker", "#!/bin/sh\nexit 0\n")

	r, err := Auto()
	if err != nil {
		t.Fatalf("Auto: %v", err)
	}
	if r.Name() != "podman" {
		t.Errorf("Name = %q, want podman", r.Name())
	}
}

func TestAutoFallsBackToDocker(t *testing.T) {
	fakeRuntime(t, "docker", "#!/bin/sh\nexit 0\n")
	dir := t.TempDir() // shadow any real podman
	t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))

	r, err := Auto()
	if err != nil {
		t.Fatalf("Auto: %v", err)
	}
	if r.Name() != "docker" {
		t.Errorf("Name = %q, want docker", r.Name())
	}
}

func TestAvailable(t *testing.T) {
	fakeRuntime(t, "podman", "#!/bin/sh\n[ \"$1\" = info ] && exit 0\nexit 1\n")
	r, err := Named("podman")
	if err != nil {
		t.Fatalf("Named: %v", err)
	}
	if err := r.Available(); err != nil {
		t.Errorf("Available: %v", err)
	}
}

func TestAvailableUnreachable(t *testing.T) {
	fakeRuntime(t, "podman", "#!/bin/sh\nexit 1\n")
	r, err := Named("podman")
	if err != nil {
		t.Fatalf("Named: %v", err)
	}
	err = r.Available()
	if err == nil || !strings.Contains(err.Error(), "podman machine start") {
		t.Errorf("Available err = %v, want unreachable message with remedy", err)
	}
}

func TestRunAttachedExitCode(t *testing.T) {
	fakeRuntime(t, "podman", "#!/bin/sh\necho out-$1\nexit 7\n")
	r, err := Named("podman")
	if err != nil {
		t.Fatalf("Named: %v", err)
	}
	var stdout bytes.Buffer
	code, err := r.RunAttached([]string{"run", "x"}, strings.NewReader(""), &stdout, &bytes.Buffer{})
	if err != nil {
		t.Fatalf("RunAttached: %v", err)
	}
	if code != 7 {
		t.Errorf("exit code = %d, want 7", code)
	}
	if strings.TrimSpace(stdout.String()) != "out-run" {
		t.Errorf("stdout = %q, want out-run", stdout.String())
	}
}

func TestListManaged(t *testing.T) {
	fakeRuntime(t, "podman", `#!/bin/sh
if [ "$1" = ps ]; then
  printf 'abc123\timg1\texited\tc1\ndef456\timg2\trunning\tc2\n'
  exit 0
fi
exit 1
`)
	r, err := Named("podman")
	if err != nil {
		t.Fatalf("Named: %v", err)
	}
	containers, err := r.ListManaged()
	if err != nil {
		t.Fatalf("ListManaged: %v", err)
	}
	if len(containers) != 2 {
		t.Fatalf("got %d containers, want 2", len(containers))
	}
	if containers[0].ID != "abc123" || containers[0].State != "exited" {
		t.Errorf("containers[0] = %+v", containers[0])
	}
	if containers[1].State != "running" {
		t.Errorf("containers[1].State = %q, want running", containers[1].State)
	}
}

func TestImageExists(t *testing.T) {
	fakeRuntime(t, "podman", "#!/bin/sh\n[ \"$3\" = present ] && exit 0\nexit 1\n")
	r, err := Named("podman")
	if err != nil {
		t.Fatalf("Named: %v", err)
	}
	ok, err := r.ImageExists("present")
	if err != nil || !ok {
		t.Errorf("ImageExists(present) = %v, %v; want true, nil", ok, err)
	}
	ok, err = r.ImageExists("absent")
	if err != nil || ok {
		t.Errorf("ImageExists(absent) = %v, %v; want false, nil", ok, err)
	}
}
