package repo

import (
	"fmt"
	"net/http"
	"sort"
	"time"

	git_model "code.gitea.io/gitea/models/git"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/convert"
	hat_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert"
	"code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git"

	"code.gitea.io/gitea/routers/api/v1/utils"
)

func ListBranchesSlice(ctx *context.APIContext) {
	listOptions := utils.GetListOptions(ctx)
	skip, _ := listOptions.GetStartEnd()

	branches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(0, 0)
	if err != nil {
		ctx.Error(http.StatusInternalServerError, "GetBranches", err)
		return
	}

	apiBranches := make([]*git.Branch, len(branches))
	for i, branch := range branches {
		c, err := branch.GetCommit()
		if err != nil {
			ctx.Error(http.StatusInternalServerError, "GetCommit", err)
			return
		}
		branchProtection, err := git_model.GetProtectedBranchBy(ctx, ctx.Repo.Repository.ID, branch.Name)
		if err != nil {
			ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
			return
		}
		sBranch, err := convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
		if err != nil {
			ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
			return
		}
		gitBranch := &git.Branch{}
		gitBranch.Branch = sBranch
		gitBranch.CommitID = c.ID.String()
		gitBranch.CommitTime = c.Author.When.Format(time.RFC3339)
		gitBranch.DefaultBranch = ctx.Repo.Repository.DefaultBranch
		gitBranch.BranchKind = int(git.OtherBranch)
		apiBranches[i] = gitBranch
		if branchProtection != nil {
			apiBranches[i].BranchKind = int(git.ProtectedBranch)
		}
		if branch.Name == ctx.Repo.Repository.DefaultBranch {
			apiBranches[i].BranchKind = int(git.DefaultBranch)
		}
		sort.Sort(git.SortBranch(apiBranches))
		branchSlice := paginate(apiBranches, skip, listOptions.Page)
		BranchesSlice := BranchesSliceByProtection(ctx, branchSlice)

		ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
		ctx.RespHeader().Set("X-Total-Count", fmt.Sprintf("%d", totalNumOfBranches))
		ctx.RespHeader().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
		ctx.JSON(http.StatusOK, &BranchesSlice)
	}
}

func paginate(branchSlice []*git.Branch, skip, pageSize int) []*git.Branch {
	limit := func() int {
		if skip+pageSize > len(branchSlice) {
			return len(branchSlice)
		} else {
			return skip + pageSize
		}
	}
	start := func() int {
		if skip > len(branchSlice) {
			return len(branchSlice)
		} else {
			return skip
		}
	}
	return branchSlice[start():limit()]

}

func BranchesSliceByProtection(ctx *context.APIContext, branchList []*git.Branch) []git.BranchesSlice {
	// group by protection
	sort.Sort(git.SortBranch(branchList))
	branchSlice := make([]git.BranchesSlice, 0)
	i := 0
	var j int
	for {
		if i >= len(branchList) {
			break
		}
		for j = i + 1; j < len(branchList) && (branchList[i].BranchKind == branchList[j].BranchKind); j++ {
		}
		// get the same branches
		sameBranchSlice := branchList[i:j]
		// sort by time
		sort.Sort(git.SortBranchTime(sameBranchSlice))
		branchSlice = append(branchSlice, git.BranchesSlice{
			BranchName: hat_convert.ToBranchType(branchList[i].BranchKind).Name(),
			Branches:   sameBranchSlice,
		})
		i = j
	}
	return branchSlice
}
