package git

import (
	"context"
	"fmt"
	"io"
	"strings"

	"gitlab.com/gitlab-org/gitaly/v16/internal/command"
	"gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
)

// WriteBlobConfig is the configuration used to write blobs into the repository.
type WriteBlobConfig struct {
	//  Path is used by git to decide which filters to run on the content.
	Path string
}

// WriteBlob writes a blob into the given repository.
func WriteBlob(ctx context.Context, repoExecutor RepositoryExecutor, content io.Reader, cfg WriteBlobConfig) (ObjectID, error) {
	options := []Option{
		Flag{Name: "--stdin"},
		Flag{Name: "-w"},
	}
	if cfg.Path != "" {
		options = append(options, ValueFlag{Name: "--path", Value: cfg.Path})
	}

	var stdout, stderr strings.Builder
	cmd, err := repoExecutor.Exec(ctx,
		Command{
			Name:  "hash-object",
			Flags: options,
		},
		WithStdin(content),
		WithStdout(&stdout),
		WithStderr(&stderr),
	)
	if err != nil {
		return "", fmt.Errorf("executing hash-object: %w", err)
	}

	if err := cmd.Wait(); err != nil {
		if exitStatus, ok := command.ExitStatus(err); ok {
			return "", structerr.New("writing blob failed with error code %d", exitStatus).WithMetadata("stderr", stderr.String())
		}

		return "", structerr.New("writing blob: %w", err)
	}

	objectHash, err := repoExecutor.ObjectHash(ctx)
	if err != nil {
		return "", fmt.Errorf("detecting object hash: %w", err)
	}

	oid, err := objectHash.FromHex(strings.TrimSuffix(stdout.String(), "\n"))
	if err != nil {
		return "", fmt.Errorf("reading blob object ID: %w", err)
	}

	return oid, nil
}
