package git

import (
	"fmt"
	"os"
	"path/filepath"

	gogit "github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/transport"
	"github.com/go-git/go-git/v5/plumbing/transport/http"
)

// Service handles Git operations for projects.
type Service struct{}

// New creates a new Git Service.
func New() *Service {
	return &Service{}
}

// CloneOrPull clones a repo if it doesn't exist locally, or pulls latest changes.
// Returns the commit hash of HEAD after the operation.
func (s *Service) CloneOrPull(url, branch, username, password, dstDir string) (string, error) {
	// Check if repo already exists
	repo, err := gogit.PlainOpen(dstDir)
	if err == nil {
		// Repo exists, pull
		return s.pull(repo, branch, username, password)
	}

	// Clone fresh
	return s.clone(url, branch, username, password, dstDir)
}

func (s *Service) clone(url, branch, username, password, dstDir string) (string, error) {
	// Ensure parent directory exists
	os.MkdirAll(filepath.Dir(dstDir), 0755)
	// Remove target if it exists (shouldn't, but be safe)
	os.RemoveAll(dstDir)

	var auth transport.AuthMethod
	if username != "" || password != "" {
		auth = &http.BasicAuth{
			Username: username,
			Password: password,
		}
	}

	refName := plumbing.NewBranchReferenceName(branch)
	repo, err := gogit.PlainClone(dstDir, false, &gogit.CloneOptions{
		URL:           url,
		Auth:          auth,
		ReferenceName: refName,
		SingleBranch:  true,
		Depth:         1,
		Progress:      nil,
	})
	if err != nil {
		return "", fmt.Errorf("clone failed: %w", err)
	}

	return getHeadHash(repo)
}

func (s *Service) pull(repo *gogit.Repository, branch, username, password string) (string, error) {
	w, err := repo.Worktree()
	if err != nil {
		return "", fmt.Errorf("worktree: %w", err)
	}

	var auth transport.AuthMethod
	if username != "" || password != "" {
		auth = &http.BasicAuth{
			Username: username,
			Password: password,
		}
	}

	refName := plumbing.NewBranchReferenceName(branch)
	err = w.Pull(&gogit.PullOptions{
		RemoteName:    "origin",
		Auth:          auth,
		ReferenceName: refName,
		SingleBranch:  true,
	})
	if err != nil && err != gogit.NoErrAlreadyUpToDate {
		return "", fmt.Errorf("pull failed: %w", err)
	}

	return getHeadHash(repo)
}

func getHeadHash(repo *gogit.Repository) (string, error) {
	ref, err := repo.Head()
	if err != nil {
		return "", err
	}
	return ref.Hash().String(), nil
}
