package projectsync

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

// ErrWorkspaceProvisionNotReady is returned when a workspace is not prepared.
var ErrWorkspaceProvisionNotReady = errors.New("workspace provision is not ready")

// WorkspaceProvisioner defines the interface for ensuring that the required
// workspace paths and Git metadata are prepared.
type WorkspaceProvisioner interface {
	EnsureProvisioned(ctx context.Context, plan ProvisionPlan) error
}

// FilesystemWorkspaceProvisioner checks the local filesystem to verify
// that the workspace topology matches the plan.
type FilesystemWorkspaceProvisioner struct{}

// EnsureProvisioned checks that the workspace root is a directory,
// and that main branch, develop branch, and default slot paths are directories
// containing a .git file or directory.
func (FilesystemWorkspaceProvisioner) EnsureProvisioned(ctx context.Context, plan ProvisionPlan) error {
	if err := ctx.Err(); err != nil {
		return err
	}

	if err := ensureDirectory(plan.ProjectWorkspaceRoot); err != nil {
		return err
	}

	for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath, plan.DefaultSlotPath} {
		if err := ctx.Err(); err != nil {
			return err
		}
		if err := ensureGitCheckout(path); err != nil {
			return err
		}
	}

	return nil
}

func ensureDirectory(path string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("%w: path %q is not accessible: %v", ErrWorkspaceProvisionNotReady, path, err)
	}
	if !info.IsDir() {
		return fmt.Errorf("%w: path %q is not a directory", ErrWorkspaceProvisionNotReady, path)
	}
	return nil
}

func ensureGitCheckout(path string) error {
	if err := ensureDirectory(path); err != nil {
		return err
	}

	gitPath := filepath.Join(path, ".git")
	_, err := os.Stat(gitPath)
	if err != nil {
		return fmt.Errorf("%w: git metadata is missing at %q: %v", ErrWorkspaceProvisionNotReady, gitPath, err)
	}
	return nil
}
