package convert

import (
	"context"
	"errors"
	"strings"

	"code.gitea.io/gitea/models/issues"
	"code.gitea.io/gitea/models/user"
	gitea_convert "code.gitea.io/gitea/modules/convert"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
	hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
)

func ToAPIPullRequest(ctx context.Context, gitRepo *git.Repository, pr *issues.PullRequest, doer *user.User) (*hat_api.PullRequest, error) {
	giteaAPIPullrequest := gitea_convert.ToAPIPullRequest(ctx, pr, doer)
	hatAPIPullrequest := &hat_api.PullRequest{
		ID:                  giteaAPIPullrequest.ID,
		URL:                 giteaAPIPullrequest.URL,
		Index:               giteaAPIPullrequest.Index,
		Poster:              giteaAPIPullrequest.Poster,
		Title:               giteaAPIPullrequest.Title,
		Body:                giteaAPIPullrequest.Body,
		Labels:              giteaAPIPullrequest.Labels,
		Milestone:           giteaAPIPullrequest.Milestone,
		Assignee:            giteaAPIPullrequest.Assignee,
		Assignees:           giteaAPIPullrequest.Assignees,
		State:               giteaAPIPullrequest.State,
		IsLocked:            giteaAPIPullrequest.IsLocked,
		Comments:            giteaAPIPullrequest.Comments,
		HTMLURL:             giteaAPIPullrequest.HTMLURL,
		DiffURL:             giteaAPIPullrequest.DiffURL,
		PatchURL:            giteaAPIPullrequest.PatchURL,
		Mergeable:           giteaAPIPullrequest.Mergeable,
		HasMerged:           giteaAPIPullrequest.HasMerged,
		Merged:              giteaAPIPullrequest.Merged,
		MergedCommitID:      giteaAPIPullrequest.MergedCommitID,
		MergedBy:            giteaAPIPullrequest.MergedBy,
		AllowMaintainerEdit: giteaAPIPullrequest.AllowMaintainerEdit,
		Base:                giteaAPIPullrequest.Base,
		Head:                giteaAPIPullrequest.Head,
		MergeBase:           giteaAPIPullrequest.MergeBase,
		Deadline:            giteaAPIPullrequest.Deadline,
		Created:             giteaAPIPullrequest.Created,
		Updated:             giteaAPIPullrequest.Updated,
		Closed:              giteaAPIPullrequest.Closed,
	}
	issue := pr.Issue
	if issue == nil {
		return hatAPIPullrequest, errors.New("pullrequest's issue is blank.")
	}

	var compareInfo *git.CompareInfo
	var err error
	if pr.HasMerged {
		var baseCommit string
		if pr.MergeBase == "" {
			var commitSHA, parentCommit string
			// If there is a head or a patch file, and it is readable, grab info
			commitSHA, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
			if err != nil {
				// Head File does not exist, try the patch
				commitSHA, err = gitRepo.ReadPatchCommit(pr.Index)
				if err == nil {
					// Recreate pull head in files for next time
					if err := gitRepo.SetReference(pr.GetGitRefName(), commitSHA); err != nil {
						log.Error("Could not write head file", err)
					}
				} else {
					// There is no history available
					log.Trace("No history file available for PR %d", pr.Index)
				}
			}
			if commitSHA != "" {
				// Get immediate parent of the first commit in the patch, grab history back
				parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(&git.RunOpts{Dir: gitRepo.Path})
				if err == nil {
					parentCommit = strings.TrimSpace(parentCommit)
				}
				// Special case on Git < 2.25 that doesn't fail on immediate empty history
				if err != nil || parentCommit == "" {
					log.Info("No known parent commit for PR %d, error: %v", pr.Index, err)
					// bring at least partial history if it can work
					parentCommit = commitSHA
				}
			}
			baseCommit = parentCommit
		} else {
			baseCommit = pr.MergeBase
		}
		compareInfo, err = gitRepo.GetCompareInfo(gitRepo.Path, baseCommit, pr.GetGitRefName(), false, false)
	} else {
		compareInfo, err = gitRepo.GetCompareInfo(gitRepo.Path, pr.MergeBase, pr.GetGitRefName(), false, false)

	}
	if err != nil {
		return hatAPIPullrequest, err
	}
	hatAPIPullrequest.CommitNum = len(compareInfo.Commits)
	hatAPIPullrequest.ChangedFiles = compareInfo.NumFiles

	return hatAPIPullrequest, nil
}
