package workspace

import (
	"context"
	"os"
	"os/exec"
	"path/filepath"
	"slices"
	"testing"
)

// seedRepo creates a tiny git repo with one tracked file, one
// gitignored file, and one untracked-non-ignored file. Returns the
// repo root path. Skips the test if git isn't on PATH (unusual for
// a dev machine, but polite).
func seedRepo(t *testing.T) string {
	t.Helper()
	if _, err := exec.LookPath("git"); err != nil {
		t.Skipf("git not on PATH: %v", err)
	}
	dir := t.TempDir()
	run := func(args ...string) {
		t.Helper()
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Dir = dir
		// Isolate from the ambient user config so commits don't need
		// a global user.name/user.email. Also disable GPG signing.
		cmd.Env = append(os.Environ(),
			"GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t",
			"GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@t",
			"GIT_CONFIG_GLOBAL=/dev/null",
		)
		if out, err := cmd.CombinedOutput(); err != nil {
			t.Fatalf("%v: %v\n%s", args, err, out)
		}
	}
	writeFile := func(relPath, content string) {
		t.Helper()
		if err := os.WriteFile(filepath.Join(dir, relPath), []byte(content), 0o644); err != nil {
			t.Fatal(err)
		}
	}
	run("git", "init", "-q", "-b", "main")
	run("git", "config", "commit.gpgsign", "false")
	writeFile(".gitignore", "ignored.log\n")
	writeFile("README.md", "hello\n")
	run("git", "add", ".gitignore", "README.md")
	run("git", "commit", "-q", "-m", "init")
	// A tracked file AFTER the first commit so ls-files picks it up.
	// A gitignored file so --exclude-standard filters it.
	// An untracked non-ignored file so the flag matters.
	writeFile("src.go", "package main\n")
	run("git", "add", "src.go")
	run("git", "commit", "-q", "-m", "src")
	writeFile("ignored.log", "noisy\n")
	writeFile("SECRETS.env", "TOKEN=abc\n")
	return dir
}

func TestListOverlayPaths_TrackedOnlyByDefault(t *testing.T) {
	repo := seedRepo(t)
	i := NewInspector()
	got, err := i.ListOverlayPaths(context.Background(), repo, false)
	if err != nil {
		t.Fatalf("ListOverlayPaths: %v", err)
	}
	want := []string{".gitignore", "README.md", "src.go"}
	if !slices.Equal(got, want) {
		t.Fatalf("got %v, want %v (untracked SECRETS.env must be excluded; gitignored ignored.log must always be excluded)", got, want)
	}
}

func TestListOverlayPaths_IncludeUntracked(t *testing.T) {
	repo := seedRepo(t)
	i := NewInspector()
	got, err := i.ListOverlayPaths(context.Background(), repo, true)
	if err != nil {
		t.Fatalf("ListOverlayPaths: %v", err)
	}
	want := []string{".gitignore", "README.md", "SECRETS.env", "src.go"}
	if !slices.Equal(got, want) {
		t.Fatalf("got %v, want %v", got, want)
	}
	// gitignored files must stay out even when untracked is included.
	for _, p := range got {
		if p == "ignored.log" {
			t.Fatalf("gitignored file leaked into overlay: %v", got)
		}
	}
}

func TestCountUntrackedPaths(t *testing.T) {
	repo := seedRepo(t)
	i := NewInspector()
	count, err := i.CountUntrackedPaths(context.Background(), repo)
	if err != nil {
		t.Fatalf("CountUntrackedPaths: %v", err)
	}
	if count != 1 {
		t.Fatalf("count = %d, want 1 (only SECRETS.env; ignored.log is gitignored)", count)
	}
}
