// Copyright 2015 - Present, The Gogs Authors. All rights reserved.
// Copyright 2018 - Present, Gitote. All rights reserved.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

package repo

import (
	"gitote/gitote/models"
	"gitote/gitote/pkg/context"
	"gitote/gitote/pkg/tool"
	"time"

	raven "github.com/getsentry/raven-go"
	"gitlab.com/gitote/git-module"
	api "gitlab.com/gitote/go-gitote-client"
	log "gopkg.in/clog.v1"
)

const (
	// BranchesOverviewTPL page template
	BranchesOverviewTPL = "repo/branches/overview"

	// BranchesAllTPL page template
	BranchesAllTPL = "repo/branches/all"
)

// Branch contains the branch information
type Branch struct {
	Name        string
	Commit      *git.Commit
	IsProtected bool
}

func loadBranches(c *context.Context) []*Branch {
	rawBranches, err := c.Repo.Repository.GetBranches()
	if err != nil {
		c.Handle(500, "GetBranches", err)
		return nil
	}

	protectBranches, err := models.GetProtectBranchesByRepoID(c.Repo.Repository.ID)
	if err != nil {
		c.Handle(500, "GetProtectBranchesByRepoID", err)
		return nil
	}

	branches := make([]*Branch, len(rawBranches))
	for i := range rawBranches {
		commit, err := rawBranches[i].GetCommit()
		if err != nil {
			c.Handle(500, "GetCommit", err)
			return nil
		}

		branches[i] = &Branch{
			Name:   rawBranches[i].Name,
			Commit: commit,
		}

		for j := range protectBranches {
			if branches[i].Name == protectBranches[j].Name {
				branches[i].IsProtected = true
				break
			}
		}
	}

	c.Data["AllowPullRequest"] = c.Repo.Repository.AllowsPulls()
	return branches
}

// Branches shows branches page
func Branches(c *context.Context) {
	c.Data["Title"] = c.Tr("repo.git_branches")
	c.Data["PageIsBranchesOverview"] = true

	branches := loadBranches(c)
	if c.Written() {
		return
	}

	now := time.Now()
	activeBranches := make([]*Branch, 0, 3)
	staleBranches := make([]*Branch, 0, 3)
	for i := range branches {
		switch {
		case branches[i].Name == c.Repo.BranchName:
			c.Data["DefaultBranch"] = branches[i]
		case branches[i].Commit.Committer.When.Add(30 * 24 * time.Hour).After(now): // 30 days
			activeBranches = append(activeBranches, branches[i])
		case branches[i].Commit.Committer.When.Add(3 * 30 * 24 * time.Hour).Before(now): // 90 days
			staleBranches = append(staleBranches, branches[i])
		}
	}

	c.Data["ActiveBranches"] = activeBranches
	c.Data["StaleBranches"] = staleBranches
	c.HTML(200, BranchesOverviewTPL)
}

// AllBranches shows all branches page
func AllBranches(c *context.Context) {
	c.Data["Title"] = c.Tr("repo.git_branches")
	c.Data["PageIsBranchesAll"] = true

	branches := loadBranches(c)
	if c.Written() {
		return
	}
	c.Data["Branches"] = branches

	c.HTML(200, BranchesAllTPL)
}

// DeleteBranchPost deletes a branch
func DeleteBranchPost(c *context.Context) {
	branchName := c.Params("*")
	commitID := c.Query("commit")

	defer func() {
		redirectTo := c.Query("redirect_to")
		if !tool.IsSameSiteURLPath(redirectTo) {
			redirectTo = c.Repo.RepoLink
		}
		c.Redirect(redirectTo)
	}()

	if !c.Repo.GitRepo.IsBranchExist(branchName) {
		return
	}
	if len(commitID) > 0 {
		branchCommitID, err := c.Repo.GitRepo.GetBranchCommitID(branchName)
		if err != nil {
			raven.CaptureErrorAndWait(err, nil)
			log.Error(2, "Failed to get commit ID of branch %q: %v", branchName, err)
			return
		}

		if branchCommitID != commitID {
			c.Flash.Error(c.Tr("repo.pulls.delete_branch_has_new_commits"))
			return
		}
	}

	if err := c.Repo.GitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
		Force: true,
	}); err != nil {
		raven.CaptureErrorAndWait(err, nil)
		log.Error(2, "Failed to delete branch %q: %v", branchName, err)
		return
	}

	if err := models.PrepareWebhooks(c.Repo.Repository, models.HookEventDelete, &api.DeletePayload{
		Ref:        branchName,
		RefType:    "branch",
		PusherType: api.PUSHER_TYPE_USER,
		Repo:       c.Repo.Repository.APIFormat(nil),
		Sender:     c.User.APIFormat(),
	}); err != nil {
		return
	}
}
