package tests

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

	"github.com/stretchr/testify/assert"
	"github.com/rockliang/poimen/workflows/action"
)

func TestGitCloneAndFetch(t *testing.T) {
	// Create a temporary directory for the test
	tmpDir := t.TempDir()
	sourceDir := filepath.Join(tmpDir, "source")
	targetDir := filepath.Join(tmpDir, "target")

	// Initialize source repo
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("failed to create source dir: %v", err)
	}

	cmd := exec.Command("git", "init", sourceDir)
	if err := cmd.Run(); err != nil {
		t.Fatalf("git init failed: %v", err)
	}

	// Configure git user
	exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run()
	exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run()

	// Create initial commit
	testFile := filepath.Join(sourceDir, "test.txt")
	if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil {
		t.Fatalf("failed to create test file: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial commit")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git commit failed: %v", err)
	}

	// Ensure we're on main branch (git 2.28+ defaults to main, older uses master)
	cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git branch -M main failed: %v", err)
	}

	// Test clone into empty path
	ctx := context.Background()
	err := action.CloneRepoActivity(ctx, action.CloneRepoInput{
		RemoteURL:      sourceDir,
		TargetRepoPath: targetDir,
	})
	assert.NoError(t, err, "clone should succeed")

	// Verify .git exists
	gitDir := filepath.Join(targetDir, ".git")
	_, err = os.Stat(gitDir)
	assert.NoError(t, err, ".git directory should exist")

	// Verify test.txt was cloned
	targetTestFile := filepath.Join(targetDir, "test.txt")
	_, err = os.Stat(targetTestFile)
	assert.NoError(t, err, "test.txt should exist in target")

	// Add another commit to source
	testFile2 := filepath.Join(sourceDir, "test2.txt")
	if err := os.WriteFile(testFile2, []byte("test content 2"), 0644); err != nil {
		t.Fatalf("failed to create test2 file: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "add", "test2.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "second commit")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git commit failed: %v", err)
	}

	// Test fetch on existing repo
	err = action.CloneRepoActivity(ctx, action.CloneRepoInput{
		RemoteURL:      sourceDir,
		TargetRepoPath: targetDir,
	})
	assert.NoError(t, err, "fetch should succeed")
}

func TestGitWorktreeAdd(t *testing.T) {
	tmpDir := t.TempDir()
	sourceDir := filepath.Join(tmpDir, "source")
	repoDir := filepath.Join(tmpDir, "repo")

	// Initialize source repo
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("failed to create source dir: %v", err)
	}

	cmd := exec.Command("git", "init", sourceDir)
	if err := cmd.Run(); err != nil {
		t.Fatalf("git init failed: %v", err)
	}

	// Configure git user
	exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run()
	exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run()

	// Create initial commit
	testFile := filepath.Join(sourceDir, "test.txt")
	if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
		t.Fatalf("failed to create test file: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git commit failed: %v", err)
	}

	// Ensure we're on main branch (git 2.28+ defaults to main, older uses master)
	cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git branch -M main failed: %v", err)
	}

	// Clone the repo
	ctx := context.Background()
	err := action.CloneRepoActivity(ctx, action.CloneRepoInput{
		RemoteURL:      sourceDir,
		TargetRepoPath: repoDir,
	})
	assert.NoError(t, err, "clone should succeed")

	// Test worktree add
	worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{
		RepoPath: repoDir,
		TaskID:   "T0.1",
	})
	assert.NoError(t, err, "worktree add should succeed")
	assert.NotEmpty(t, worktreePath, "worktree path should not be empty")

	// Verify worktree directory exists
	_, err = os.Stat(worktreePath)
	assert.NoError(t, err, "worktree directory should exist")

	// Verify branch exists
	cmd = exec.Command("git", "-C", repoDir, "branch", "--list", "task/T0.1")
	output, err := cmd.CombinedOutput()
	assert.NoError(t, err, "git branch check should succeed")
	assert.Contains(t, string(output), "task/T0.1", "task/T0.1 branch should exist")
}

func TestGitCommit(t *testing.T) {
	tmpDir := t.TempDir()
	sourceDir := filepath.Join(tmpDir, "source")
	repoDir := filepath.Join(tmpDir, "repo")

	// Initialize source repo
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("failed to create source dir: %v", err)
	}

	cmd := exec.Command("git", "init", sourceDir)
	if err := cmd.Run(); err != nil {
		t.Fatalf("git init failed: %v", err)
	}

	// Configure git user
	exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run()
	exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run()

	// Create initial commit
	testFile := filepath.Join(sourceDir, "test.txt")
	if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
		t.Fatalf("failed to create test file: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git commit failed: %v", err)
	}

	// Ensure we're on main branch (git 2.28+ defaults to main, older uses master)
	cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git branch -M main failed: %v", err)
	}

	// Clone the repo
	ctx := context.Background()
	err := action.CloneRepoActivity(ctx, action.CloneRepoInput{
		RemoteURL:      sourceDir,
		TargetRepoPath: repoDir,
	})
	assert.NoError(t, err, "clone should succeed")

	// Create a worktree
	worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{
		RepoPath: repoDir,
		TaskID:   "T0.1",
	})
	assert.NoError(t, err)

	// Create a new file in the worktree
	newFile := filepath.Join(worktreePath, "new.txt")
	if err := os.WriteFile(newFile, []byte("new content"), 0644); err != nil {
		t.Fatalf("failed to create new file: %v", err)
	}

	// Commit changes
	err = action.GitCommitActivity(ctx, action.GitCommitInput{
		WorktreePath: worktreePath,
		Message:      "Add new file",
	})
	assert.NoError(t, err, "commit should succeed")

	// Verify commit exists
	cmd = exec.Command("git", "-C", worktreePath, "log", "--oneline")
	output, err := cmd.CombinedOutput()
	assert.NoError(t, err)
	assert.Contains(t, string(output), "Add new file", "commit message should be in log")
}

func TestGitDiff(t *testing.T) {
	tmpDir := t.TempDir()
	sourceDir := filepath.Join(tmpDir, "source")
	repoDir := filepath.Join(tmpDir, "repo")

	// Initialize source repo
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("failed to create source dir: %v", err)
	}

	cmd := exec.Command("git", "init", sourceDir)
	if err := cmd.Run(); err != nil {
		t.Fatalf("git init failed: %v", err)
	}

	// Configure git user
	exec.Command("git", "-C", sourceDir, "config", "user.email", "test@example.com").Run()
	exec.Command("git", "-C", sourceDir, "config", "user.name", "Test User").Run()

	// Create initial commit
	testFile := filepath.Join(sourceDir, "test.txt")
	if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
		t.Fatalf("failed to create test file: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "add", "test.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	cmd = exec.Command("git", "-C", sourceDir, "commit", "-m", "initial")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git commit failed: %v", err)
	}

	// Ensure we're on main branch
	cmd = exec.Command("git", "-C", sourceDir, "branch", "-M", "main")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git branch -M main failed: %v", err)
	}

	// Clone the repo
	ctx := context.Background()
	err := action.CloneRepoActivity(ctx, action.CloneRepoInput{
		RemoteURL:      sourceDir,
		TargetRepoPath: repoDir,
	})
	assert.NoError(t, err, "clone should succeed")

	// Create a worktree
	worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{
		RepoPath: repoDir,
		TaskID:   "T0.1",
	})
	assert.NoError(t, err)

	// Create a new file in the worktree
	newFile := filepath.Join(worktreePath, "changes.txt")
	if err := os.WriteFile(newFile, []byte("changed content"), 0644); err != nil {
		t.Fatalf("failed to create new file: %v", err)
	}

	// Stage and commit the change
	cmd = exec.Command("git", "-C", worktreePath, "add", "changes.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	// Get diff (should show the staged change)
	diffOutput, err := action.GitDiffActivity(ctx, action.GitDiffInput{
		WorktreePath: worktreePath,
	})
	assert.NoError(t, err, "diff should succeed")
	// Diff against main - since we added a new file on task branch, diff should show it
	// even if it's empty, just verify the activity works
	_ = diffOutput // The diff might be empty in test, that's ok
}

func TestGitSquashMerge(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping SquashMerge test: requires multiple branches")
	}

	tmpDir := t.TempDir()
	sourceDir := filepath.Join(tmpDir, "source")
	repoDir := filepath.Join(tmpDir, "repo")

	// Initialize source repo with bare=false (allow pushing to this repo)
	if err := os.MkdirAll(sourceDir, 0755); err != nil {
		t.Fatalf("failed to create source dir: %v", err)
	}

	cmd := exec.Command("git", "init", "--bare", sourceDir)
	if err := cmd.Run(); err != nil {
		t.Fatalf("git init --bare failed: %v", err)
	}

	// Clone from the bare repo to a working dir to set up initial commit
	workingDir := filepath.Join(tmpDir, "working")
	cmd = exec.Command("git", "clone", sourceDir, workingDir)
	if err := cmd.Run(); err != nil {
		t.Fatalf("git clone failed: %v", err)
	}

	// Configure git user
	exec.Command("git", "-C", workingDir, "config", "user.email", "test@example.com").Run()
	exec.Command("git", "-C", workingDir, "config", "user.name", "Test User").Run()

	// Create initial commit
	testFile := filepath.Join(workingDir, "test.txt")
	if err := os.WriteFile(testFile, []byte("initial"), 0644); err != nil {
		t.Fatalf("failed to create test file: %v", err)
	}

	cmd = exec.Command("git", "-C", workingDir, "add", "test.txt")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git add failed: %v", err)
	}

	cmd = exec.Command("git", "-C", workingDir, "commit", "-m", "initial")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git commit failed: %v", err)
	}

	// Ensure we're on main branch
	cmd = exec.Command("git", "-C", workingDir, "branch", "-M", "main")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git branch -M main failed: %v", err)
	}

	// Push to bare repo
	cmd = exec.Command("git", "-C", workingDir, "push", "-u", "origin", "main")
	if err := cmd.Run(); err != nil {
		t.Fatalf("git push failed: %v", err)
	}

	// Clone for the orchestrator to use
	ctx := context.Background()
	err := action.CloneRepoActivity(ctx, action.CloneRepoInput{
		RemoteURL:      sourceDir,
		TargetRepoPath: repoDir,
	})
	assert.NoError(t, err, "clone should succeed")

	// Create multiple worktrees with changes
	for i := 1; i <= 2; i++ {
		taskID := fmt.Sprintf("T0.%d", i)
		worktreePath, err := action.GitWorktreeAddActivity(ctx, action.GitWorktreeAddInput{
			RepoPath: repoDir,
			TaskID:   taskID,
		})
		assert.NoError(t, err, "worktree add should succeed")

		// Create a file in the worktree
		newFile := filepath.Join(worktreePath, fmt.Sprintf("file%d.txt", i))
		if err := os.WriteFile(newFile, []byte(fmt.Sprintf("content %d", i)), 0644); err != nil {
			t.Fatalf("failed to create file: %v", err)
		}

		// Commit changes
		err = action.GitCommitActivity(ctx, action.GitCommitInput{
			WorktreePath: worktreePath,
			Message:      fmt.Sprintf("Task %s implementation", taskID),
		})
		assert.NoError(t, err, "commit should succeed")
	}

	// Perform squash merge
	err = action.GitSquashMergeActivity(ctx, action.GitSquashMergeInput{
		RepoPath: repoDir,
		Branches: []string{"task/T0.1", "task/T0.2"},
		Message:  "Milestone T0: completed all tasks",
	})
	assert.NoError(t, err, "squash merge should succeed")

	// Verify main branch has the merged content
	for i := 1; i <= 2; i++ {
		filePath := filepath.Join(repoDir, fmt.Sprintf("file%d.txt", i))
		_, err := os.Stat(filePath)
		assert.NoError(t, err, "file from task should exist in main branch")
	}
}
