package git

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"
	"strings"

	gitea_git "code.gitea.io/gitea/modules/git"
)

const prettyLogFormat = `--pretty=format:%H`

func GetFirstAndLastCommitByPath(repo *gitea_git.Repository, revision, relpath string) (*gitea_git.Commit, *gitea_git.Commit, error) {
	stdout, _, runErr := gitea_git.NewCommand(repo.Ctx, "log").AddDynamicArguments(revision, prettyLogFormat, "--"+relpath).RunStdBytes(&gitea_git.RunOpts{Dir: repo.Path})
	if runErr != nil {
		return nil, nil, runErr
	}

	var commits []*gitea_git.Commit
	if len(stdout) == 0 {
		return nil, nil, nil
	}

	parts := bytes.Split(stdout, []byte{'\n'})

	for _, commitID := range parts {
		commit, err := repo.GetCommit(string(commitID))
		if err != nil {
			return nil, nil, err
		}
		commits = append(commits, commit)
	}

	return commits[0], commits[len(commits)-1], nil
}

// CommitsByFileAndRange return the commits according revision file and the page
func AllCommitsByFileAndRange(repo *gitea_git.Repository, opts gitea_git.CommitsByFileAndRangeOptions, keyword string, pageSize int) ([]*gitea_git.Commit, error) {
	skip := (opts.Page - 1) * pageSize

	stdoutReader, stdoutWriter := io.Pipe()
	defer func() {
		_ = stdoutReader.Close()
		_ = stdoutWriter.Close()
	}()
	go func() {
		stderr := strings.Builder{}
		gitCmd := gitea_git.NewCommand(repo.Ctx, "rev-list").
			AddOptionFormat("--max-count=%d", pageSize*opts.Page).
			AddOptionFormat("--skip=%d", skip).
			AddOptionFormat("--grep=%s", keyword)
		gitCmd.AddDynamicArguments(opts.Revision)

		if opts.Not != "" {
			gitCmd.AddOptionValues("--not", opts.Not)
		}

		gitCmd.AddArguments("--all")

		// gitCmd.AddDashesAndList(opts.File)
		err := gitCmd.Run(&gitea_git.RunOpts{
			Dir:    repo.Path,
			Stdout: stdoutWriter,
			Stderr: &stderr,
		})
		if err != nil {
			_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
		} else {
			_ = stdoutWriter.Close()
		}
	}()

	commits := []*gitea_git.Commit{}
	shaline := [41]byte{}
	var sha1 gitea_git.SHA1
	for {
		n, err := io.ReadFull(stdoutReader, shaline[:])
		if err != nil || n < 40 {
			if err == io.EOF {
				err = nil
			}
			return commits, err
		}
		n, err = hex.Decode(sha1[:], shaline[0:40])
		if n != 20 {
			err = fmt.Errorf("invalid sha %q", string(shaline[:40]))
		}
		if err != nil {
			return nil, err
		}
		commit, err := repo.GetCommit(sha1.String())
		if err != nil {
			return nil, err
		}
		commits = append(commits, commit)
	}
}

func GetAllCommitsCount(repo *gitea_git.Repository, keyword string) (int64, error) {
	cmd := gitea_git.NewCommand(repo.Ctx, "rev-list").
		AddOptionFormat("--grep=%s", keyword)
	cmd.AddArguments("--exclude=" + "refs/pull/" + "*")

	cmd.AddArguments("--all", "--count")

	stdout, _, err := cmd.RunStdString(&gitea_git.RunOpts{Dir: repo.Path})
	if err != nil {
		return 0, err
	}

	return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
}
