// Copyright 2020 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitutil

import (
	"fmt"
	"strconv"
	"time"

	"github.com/gogs/git-module"
	"github.com/pkg/errors"
	log "unknwon.dev/clog/v2"
)

// PullRequestMeta contains metadata for a pull request.
type PullRequestMeta struct {
	// The merge base of the pull request.
	MergeBase string
	// The commits that are requested to be merged.
	Commits []*git.Commit
	// The number of files changed.
	NumFiles int
}

func (module) PullRequestMeta(headPath, basePath, headBranch, baseBranch string) (*PullRequestMeta, error) {
	tmpRemoteBranch := baseBranch

	// We need to create a temporary remote when the pull request is sent from a forked repository.
	if headPath != basePath {
		tmpRemote := strconv.FormatInt(time.Now().UnixNano(), 10)
		err := Module.RepoAddRemote(headPath, tmpRemote, basePath, git.AddRemoteOptions{Fetch: true})
		if err != nil {
			return nil, fmt.Errorf("add remote: %v", err)
		}
		defer func() {
			err := Module.RepoRemoveRemote(headPath, tmpRemote)
			if err != nil {
				log.Error("Failed to remove remote %q [path: %s]: %v", tmpRemote, headPath, err)
				return
			}
		}()

		tmpRemoteBranch = "remotes/" + tmpRemote + "/" + baseBranch
	}

	mergeBase, err := Module.RepoMergeBase(headPath, tmpRemoteBranch, headBranch)
	if err != nil {
		return nil, errors.Wrap(err, "get merge base")
	}

	commits, err := Module.RepoLog(headPath, mergeBase+"..."+headBranch)
	if err != nil {
		return nil, errors.Wrap(err, "get commits")
	}

	// Count number of changed files
	names, err := Module.RepoDiffNameOnly(headPath, tmpRemoteBranch, headBranch, git.DiffNameOnlyOptions{NeedsMergeBase: true})
	if err != nil {
		return nil, errors.Wrap(err, "get changed files")
	}

	return &PullRequestMeta{
		MergeBase: mergeBase,
		Commits:   commits,
		NumFiles:  len(names),
	}, nil
}