package testharness

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/go-git/go-billy/v5/osfs"
	git "github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/filemode"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/storage/filesystem"
	"github.com/pingcap/agent-git-service/internal/gitstore"
)

// InitializeBareRepositoryFixture arranges a bare repository for low-level
// Git tests without exposing another production mutation API. It deliberately
// does not install server policy such as receive hooks; those behaviors belong
// to repositorymutation owner tests.
func InitializeBareRepositoryFixture(ctx context.Context, store *gitstore.Store, fullName, defaultBranch string, seed bool) error {
	if store == nil || defaultBranch == "" {
		return fmt.Errorf("invalid bare repository fixture identity")
	}
	directory, err := store.GetRepoPath(ctx, fullName)
	if err != nil {
		return err
	}
	if _, err := os.Stat(directory); err == nil {
		return nil
	} else if !os.IsNotExist(err) {
		return err
	}
	if err := os.MkdirAll(directory, 0o750); err != nil {
		return err
	}
	storage := filesystem.NewStorage(osfs.New(directory), nil)
	repository, err := git.InitWithOptions(storage, nil, git.InitOptions{
		DefaultBranch: plumbing.NewBranchReferenceName(defaultBranch),
	})
	if err != nil {
		return err
	}
	if !seed {
		return nil
	}
	return seedBareRepositoryFixture(repository, defaultBranch)
}

func seedBareRepositoryFixture(repository *git.Repository, defaultBranch string) error {
	storage := repository.Storer
	blob := storage.NewEncodedObject()
	blob.SetType(plumbing.BlobObject)
	writer, err := blob.Writer()
	if err != nil {
		return err
	}
	if closeErr := writer.Close(); closeErr != nil {
		return closeErr
	}
	blobHash, err := storage.SetEncodedObject(blob)
	if err != nil {
		return err
	}

	treeObject := storage.NewEncodedObject()
	treeObject.SetType(plumbing.TreeObject)
	tree := object.Tree{Entries: []object.TreeEntry{{Name: "README.md", Mode: filemode.Regular, Hash: blobHash}}}
	if err := tree.Encode(treeObject); err != nil {
		return err
	}
	treeHash, err := storage.SetEncodedObject(treeObject)
	if err != nil {
		return err
	}

	now := time.Now()
	signature := object.Signature{Name: "gh-server", Email: "gh-server@localhost", When: now}
	commitObject := storage.NewEncodedObject()
	commitObject.SetType(plumbing.CommitObject)
	commit := object.Commit{
		Author: signature, Committer: signature, Message: "Initial commit\n", TreeHash: treeHash,
	}
	if err := commit.Encode(commitObject); err != nil {
		return err
	}
	commitHash, err := storage.SetEncodedObject(commitObject)
	if err != nil {
		return err
	}
	branch := plumbing.NewBranchReferenceName(defaultBranch)
	if err := storage.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, branch)); err != nil {
		return err
	}
	return storage.SetReference(plumbing.NewHashReference(branch, commitHash))
}
