package gitstore_test

import (
	"context"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/pingcap/agent-git-service/internal/gitstore"
)

func TestStore_InitRead(t *testing.T) {
	store, cleanup := gitstore.NewTestStore(t)
	defer cleanup()

	ctx := context.Background()
	repoName := "user/repo1"
	// Test Exists (should be false)
	if store.Exists(ctx, repoName) {
		t.Error("expected repo not to exist")
	}

	// Test Init
	if err := initializeBareRepositoryFixture(store, ctx, repoName, "main", true); err != nil {
		t.Fatalf("Init failed: %v", err)
	}

	// Test Exists (should be true)
	if !store.Exists(ctx, repoName) {
		t.Error("expected repo to exist after Init")
	}
	readme, err := store.ReadFile(ctx, repoName, "README.md")
	if err != nil {
		t.Fatalf("ReadFile README.md failed: %v", err)
	}
	if len(readme) != 0 {
		t.Fatalf("expected auto-init README.md to be empty, got %q", readme)
	}
}

func TestStore_WriteFileCommitIdentity(t *testing.T) {
	tmpDir, err := os.MkdirTemp("", "gitstore-writefile-identity-*")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpDir)

	store, err := gitstore.New(tmpDir)
	if err != nil {
		t.Fatal(err)
	}

	ctx := context.Background()
	repoName := "user/repo-identity-write"
	if err := initializeBareRepositoryFixture(store, ctx, repoName, "main", true); err != nil {
		t.Fatalf("Init failed: %v", err)
	}

	sha, err := store.WriteFile(ctx, repoName, "main", "file.txt", "test commit", []byte("content\n"))
	if err != nil {
		t.Fatalf("WriteFile failed: %v", err)
	}

	// Assert branch tip advancement
	headSHA, err := store.HeadSHA(ctx, repoName, "main")
	if err != nil {
		t.Fatalf("HeadSHA failed: %v", err)
	}
	if sha != headSHA {
		t.Fatalf("expected WriteFile SHA %s to equal HeadSHA(main) %s", sha, headSHA)
	}

	repoDir, err := store.GetRepoPath(ctx, repoName)
	if err != nil {
		t.Fatalf("GetRepoPath failed: %v", err)
	}

	// Assert file.txt exists in commit tree
	treeOutput := runGit(t, repoDir, "ls-tree", "--name-only", sha)
	if !strings.Contains(treeOutput, "file.txt") {
		t.Fatalf("expected file.txt in commit tree, got:\n%s", treeOutput)
	}

	identity := strings.TrimSpace(runGit(t, repoDir, "log", "-1", "--format=%an <%ae> %cn <%ce>", sha))
	expected := "gh-server <gh-server@localhost> gh-server <gh-server@localhost>"
	if identity != expected {
		t.Fatalf("unexpected commit identity:\n  got:  %s\n  want: %s", identity, expected)
	}
}

// TestStore_WriteFileNestedPath tests creating files in nested directories (issue #208)
func TestStore_WriteFileNestedPath(t *testing.T) {
	tmpDir, err := os.MkdirTemp("", "gitstore-nested-path-*")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpDir)

	store, err := gitstore.New(tmpDir)
	if err != nil {
		t.Fatal(err)
	}

	ctx := context.Background()
	repoName := "user/repo-nested"
	if err := initializeBareRepositoryFixture(store, ctx, repoName, "main", true); err != nil {
		t.Fatalf("Init failed: %v", err)
	}

	// Test creating a file in a nested directory (.github/workflows/ci.yml)
	nestedPath := ".github/workflows/ci.yml"
	content := []byte("name: ci\non:\n  workflow_dispatch:\n")
	sha, err := store.WriteFile(ctx, repoName, "main", nestedPath, "add workflow file", content)
	if err != nil {
		t.Fatalf("WriteFile nested path failed: %v", err)
	}

	// Assert branch tip advancement
	headSHA, err := store.HeadSHA(ctx, repoName, "main")
	if err != nil {
		t.Fatalf("HeadSHA failed: %v", err)
	}
	if sha != headSHA {
		t.Fatalf("expected WriteFile SHA %s to equal HeadSHA(main) %s", sha, headSHA)
	}

	repoDir, err := store.GetRepoPath(ctx, repoName)
	if err != nil {
		t.Fatalf("GetRepoPath failed: %v", err)
	}

	// Assert the nested file exists in commit tree
	treeOutput := runGit(t, repoDir, "ls-tree", "-r", "--name-only", sha)
	if !strings.Contains(treeOutput, nestedPath) {
		t.Fatalf("expected %s in commit tree, got:\n%s", nestedPath, treeOutput)
	}

	// Assert the content is correct
	readContent, err := store.ReadFile(ctx, repoName, nestedPath)
	if err != nil {
		t.Fatalf("ReadFile nested path failed: %v", err)
	}
	if string(readContent) != string(content) {
		t.Fatalf("content mismatch:\n  got:  %s\n  want: %s", readContent, content)
	}

	// Test creating another file in the same nested directory
	secondPath := ".github/workflows/release.yml"
	secondContent := []byte("name: release\non:\n  push:\n    tags:\n      - 'v*'\n")
	sha2, err := store.WriteFile(ctx, repoName, "main", secondPath, "add release workflow", secondContent)
	if err != nil {
		t.Fatalf("WriteFile second nested path failed: %v", err)
	}

	// Verify both files exist
	treeOutput2 := runGit(t, repoDir, "ls-tree", "-r", "--name-only", sha2)
	if !strings.Contains(treeOutput2, nestedPath) {
		t.Fatalf("expected %s in commit tree after second write, got:\n%s", nestedPath, treeOutput2)
	}
	if !strings.Contains(treeOutput2, secondPath) {
		t.Fatalf("expected %s in commit tree after second write, got:\n%s", secondPath, treeOutput2)
	}

	// Test creating a file in a deeply nested directory
	deepPath := "a/b/c/d/deep.txt"
	deepContent := []byte("deep nested content")
	sha3, err := store.WriteFile(ctx, repoName, "main", deepPath, "add deeply nested file", deepContent)
	if err != nil {
		t.Fatalf("WriteFile deeply nested path failed: %v", err)
	}

	treeOutput3 := runGit(t, repoDir, "ls-tree", "-r", "--name-only", sha3)
	if !strings.Contains(treeOutput3, deepPath) {
		t.Fatalf("expected %s in commit tree, got:\n%s", deepPath, treeOutput3)
	}
}

func runGit(t *testing.T, repoDir string, args ...string) string {
	t.Helper()
	cmd := exec.Command("git", append([]string{"-C", repoDir}, args...)...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("git %v failed: %v\n%s", args, err, string(out))
	}
	return string(out)
}
