package projectsync

import (
	"context"
	"errors"
	"os"
	"path/filepath"
	"testing"
)

func TestFilesystemWorkspaceProvisionerAcceptsPreparedCheckouts(t *testing.T) {
	tmpDir := t.TempDir()

	plan := ProvisionPlan{
		ProjectWorkspaceRoot: tmpDir,
		MainBranchPath:       filepath.Join(tmpDir, "branches", "main"),
		DevelopBranchPath:    filepath.Join(tmpDir, "branches", "develop"),
		DefaultSlotPath:      filepath.Join(tmpDir, "slots", "000"),
	}

	// Create directories
	for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath, plan.DefaultSlotPath} {
		if err := os.MkdirAll(path, 0755); err != nil {
			t.Fatalf("failed to create dir %s: %v", path, err)
		}
		// Create .git directory
		if err := os.MkdirAll(filepath.Join(path, ".git"), 0755); err != nil {
			t.Fatalf("failed to create .git in %s: %v", path, err)
		}
	}

	provisioner := FilesystemWorkspaceProvisioner{}
	err := provisioner.EnsureProvisioned(context.Background(), plan)
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
}

func TestFilesystemWorkspaceProvisionerRejectsMissingCheckout(t *testing.T) {
	tmpDir := t.TempDir()

	plan := ProvisionPlan{
		ProjectWorkspaceRoot: tmpDir,
		MainBranchPath:       filepath.Join(tmpDir, "branches", "main"),
		DevelopBranchPath:    filepath.Join(tmpDir, "branches", "develop"),
		DefaultSlotPath:      filepath.Join(tmpDir, "slots", "000"),
	}

	// Create main and develop only, skip default slot
	for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath} {
		if err := os.MkdirAll(path, 0755); err != nil {
			t.Fatalf("failed to create dir %s: %v", path, err)
		}
		if err := os.MkdirAll(filepath.Join(path, ".git"), 0755); err != nil {
			t.Fatalf("failed to create .git in %s: %v", path, err)
		}
	}

	provisioner := FilesystemWorkspaceProvisioner{}
	err := provisioner.EnsureProvisioned(context.Background(), plan)
	if !errors.Is(err, ErrWorkspaceProvisionNotReady) {
		t.Errorf("expected ErrWorkspaceProvisionNotReady error, got %v", err)
	}
}

func TestFilesystemWorkspaceProvisionerRejectsPathWithoutGitMetadata(t *testing.T) {
	tmpDir := t.TempDir()

	plan := ProvisionPlan{
		ProjectWorkspaceRoot: tmpDir,
		MainBranchPath:       filepath.Join(tmpDir, "branches", "main"),
		DevelopBranchPath:    filepath.Join(tmpDir, "branches", "develop"),
		DefaultSlotPath:      filepath.Join(tmpDir, "slots", "000"),
	}

	// Create directories but do not create .git for default slot
	for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath, plan.DefaultSlotPath} {
		if err := os.MkdirAll(path, 0755); err != nil {
			t.Fatalf("failed to create dir %s: %v", path, err)
		}
	}
	// Create .git only for main and develop
	for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath} {
		if err := os.MkdirAll(filepath.Join(path, ".git"), 0755); err != nil {
			t.Fatalf("failed to create .git in %s: %v", path, err)
		}
	}

	provisioner := FilesystemWorkspaceProvisioner{}
	err := provisioner.EnsureProvisioned(context.Background(), plan)
	if !errors.Is(err, ErrWorkspaceProvisionNotReady) {
		t.Errorf("expected ErrWorkspaceProvisionNotReady error, got %v", err)
	}
}

func TestFilesystemWorkspaceProvisionerHonorsCanceledContext(t *testing.T) {
	tmpDir := t.TempDir()

	plan := ProvisionPlan{
		ProjectWorkspaceRoot: tmpDir,
		MainBranchPath:       filepath.Join(tmpDir, "branches", "main"),
		DevelopBranchPath:    filepath.Join(tmpDir, "branches", "develop"),
		DefaultSlotPath:      filepath.Join(tmpDir, "slots", "000"),
	}

	ctx, cancel := context.WithCancel(context.Background())
	cancel() // Cancel immediately

	provisioner := FilesystemWorkspaceProvisioner{}
	err := provisioner.EnsureProvisioned(ctx, plan)
	if !errors.Is(err, context.Canceled) {
		t.Errorf("expected context.Canceled, got %v", err)
	}
}
