package git

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"strconv"
	"strings"

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

type RepoContributor struct {
	Name             string `json:"name"`
	Email            string `json:"email"`
	Commits          int    `json:"commits"`
	ContributionPerc string `json:"contribution_perc"`
}

func GetRepoContributors(repo *gitea_git.Repository, page, pageSize int) (int, []*RepoContributor, error) {
	var total, totalContributions, skip int
	var contributors []*RepoContributor

	skip = (page - 1) * pageSize

	stdoutReader, stdoutWriter, err := os.Pipe()
	if err != nil {
		return total, nil, err
	}
	defer func() {
		_ = stdoutReader.Close()
		_ = stdoutWriter.Close()
	}()
	cmd := gitea_git.NewCommand(repo.Ctx, "shortlog", "-sne", "--all")

	stderr := new(strings.Builder)
	err = cmd.Run(&gitea_git.RunOpts{
		Env:    []string{},
		Dir:    repo.Path,
		Stdout: stdoutWriter,
		Stderr: stderr,
		PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
			_ = stdoutWriter.Close()
			scanner := bufio.NewScanner(stdoutReader)
			scanner.Split(bufio.ScanLines)
			var ca int
			for scanner.Scan() {
				l := strings.TrimSpace(scanner.Text())
				commits := l[0:strings.Index(l, "\t")]
				commitsInt, err := strconv.Atoi(commits)
				if err != nil {
					return err
				}
				name := l[strings.Index(l, "\t")+1 : strings.Index(l, " <")]
				email := l[strings.Index(l, "<")+1 : strings.Index(l, ">")]
				totalContributions += commitsInt
				total++
				if skip > 0 {
					skip--
				} else {
					if ca < pageSize {
						contributors = append(contributors, &RepoContributor{
							Commits: commitsInt,
							Name:    name,
							Email:   email,
						})
						ca++
					}
				}
			}

			_ = stdoutReader.Close()
			return nil
		},
	})

	for _, cont := range contributors {
		fperc := fmt.Sprintf("%.2f", float64(cont.Commits)*100/float64(totalContributions))
		cont.ContributionPerc = fperc + "%"
	}

	return total, contributors, nil
}

func GetRepoContributorsNew(repo *gitea_git.Repository, page, pageSize int) (int, []*RepoContributor, error) {
	var total, totalContributions, skip int
	var contributors []*RepoContributor
	var contributorInfos []*RepoContributor
	contributorInfoHash := make(map[string]*RepoContributor)

	skip = (page - 1) * pageSize

	stdoutReader, stdoutWriter, err := os.Pipe()
	if err != nil {
		return total, nil, err
	}
	defer func() {
		_ = stdoutReader.Close()
		_ = stdoutWriter.Close()
	}()
	cmd := gitea_git.NewCommand(repo.Ctx, "shortlog", "-sne", "--all")

	stderr := new(strings.Builder)
	err = cmd.Run(&gitea_git.RunOpts{
		Env:    []string{},
		Dir:    repo.Path,
		Stdout: stdoutWriter,
		Stderr: stderr,
		PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
			_ = stdoutWriter.Close()
			scanner := bufio.NewScanner(stdoutReader)
			scanner.Split(bufio.ScanLines)
			for scanner.Scan() {
				l := strings.TrimSpace(scanner.Text())
				commits := l[0:strings.Index(l, "\t")]
				commitsInt, err := strconv.Atoi(commits)
				if err != nil {
					return err
				}
				name := l[strings.Index(l, "\t")+1 : strings.Index(l, " <")]
				email := l[strings.Index(l, "<")+1 : strings.Index(l, ">")]
				totalContributions += commitsInt
				// committer is not system user
				if existedContributorInfo, ok := contributorInfoHash[email]; ok {
					// existed: same primary email, different committer name
					existedContributorInfo.Commits += commitsInt
				} else {
					var newContributor = &RepoContributor{
						Commits: commitsInt,
						Name:    name,
						Email:   email,
					}
					total++
					contributorInfos = append(contributorInfos, newContributor)
					contributorInfoHash[email] = newContributor
				}
			}

			_ = stdoutReader.Close()
			return nil
		},
	})

	var ca int
	for _, cont := range contributorInfos {
		if skip > 0 {
			skip--
		} else {
			if ca < pageSize {
				fperc := fmt.Sprintf("%.2f", float64(cont.Commits)*100/float64(totalContributions))
				contributors = append(contributors, &RepoContributor{
					Commits:          cont.Commits,
					Name:             cont.Name,
					Email:            cont.Email,
					ContributionPerc: fperc + "%",
				})
				ca++
			}
		}
	}
	return total, contributors, nil
}
