package brief

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/yan1h/agent-coding-workflow/internal/infra/git"
)

func gitAvailable(t *testing.T) {
	t.Helper()
	if _, err := exec.LookPath("git"); err != nil {
		t.Skip("git not found in PATH")
	}
}

func mustGit(t *testing.T, dir string, args ...string) {
	t.Helper()
	c := exec.Command("git", args...)
	c.Dir = dir
	out, err := c.CombinedOutput()
	require.NoError(t, err, "git %v: %s", args, string(out))
}

// makeWorkRepo 在临时目录里建一个有 N 次提交、可选脏文件的工作仓库，返回路径。
func makeWorkRepo(t *testing.T, commits int, dirty bool) string {
	t.Helper()
	gitAvailable(t)
	dir := t.TempDir()
	mustGit(t, dir, "init", "-b", "main")
	mustGit(t, dir, "config", "user.email", "test@example.com")
	mustGit(t, dir, "config", "user.name", "Test")
	for i := 0; i < commits; i++ {
		name := filepath.Join(dir, "f"+string(rune('a'+i))+".txt")
		require.NoError(t, os.WriteFile(name, []byte("v"), 0o644))
		mustGit(t, dir, "add", ".")
		mustGit(t, dir, "commit", "-m", "commit "+string(rune('a'+i)))
	}
	if dirty {
		require.NoError(t, os.WriteFile(filepath.Join(dir, "dirty.txt"), []byte("x"), 0o644))
	}
	return dir
}

func TestRepoOrienter_RealRepo(t *testing.T) {
	t.Parallel()
	dir := makeWorkRepo(t, 3, true)
	o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()}))

	got := o.Orient(context.Background(), dir, "main", 10, 10)
	assert.Equal(t, "main", got.Branch)
	assert.Len(t, got.RecentCommits, 3)
	// 脏文件含未跟踪的 dirty.txt。
	require.NotEmpty(t, got.DirtyFiles)
	assert.Contains(t, got.DirtyFiles, "dirty.txt")
}

func TestRepoOrienter_BoundsCommits(t *testing.T) {
	t.Parallel()
	dir := makeWorkRepo(t, 5, false)
	o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()}))

	got := o.Orient(context.Background(), dir, "main", 2, 10)
	assert.LessOrEqual(t, len(got.RecentCommits), 2)
}

func TestRepoOrienter_NonexistentDirReturnsPartialNoFailure(t *testing.T) {
	t.Parallel()
	gitAvailable(t)
	o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()}))

	// 不存在的目录：git 调用失败，但 orienter 不 panic、不返回错误，分支仍带入。
	got := o.Orient(context.Background(), filepath.Join(t.TempDir(), "nope"), "wip", 10, 10)
	assert.Equal(t, "wip", got.Branch)
	assert.Empty(t, got.RecentCommits)
	assert.Empty(t, got.DirtyFiles)
}

func TestRepoOrienter_EmptyDirSkips(t *testing.T) {
	t.Parallel()
	o := NewRepoOrienter(git.NewDefaultRunner(git.Config{Binary: "git", TmpDir: t.TempDir()}))
	got := o.Orient(context.Background(), "", "main", 10, 10)
	assert.Equal(t, "main", got.Branch)
	assert.Empty(t, got.RecentCommits)
	assert.Empty(t, got.DirtyFiles)
}

func TestRepoOrienter_NilGitClient(t *testing.T) {
	t.Parallel()
	o := NewRepoOrienter(nil)
	got := o.Orient(context.Background(), "/tmp/x", "main", 10, 10)
	assert.Equal(t, "main", got.Branch)
	assert.Empty(t, got.RecentCommits)
}

func TestRepoOrientation_IsEmpty(t *testing.T) {
	t.Parallel()
	assert.True(t, RepoOrientation{}.IsEmpty())
	assert.False(t, RepoOrientation{Branch: "x"}.IsEmpty())
	assert.False(t, RepoOrientation{RecentCommits: []string{"a"}}.IsEmpty())
}
