// Copyright 2020 The Gitea 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 gitea

import (
	"log"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPullReview(t *testing.T) {
	log.Println("== TestPullReview ==")
	c := newTestClient()

	repoName := "Reviews"
	repo, pull, submitter, reviewer, success := preparePullReviewTest(t, c, repoName)
	if !success {
		return
	}

	// CreatePullReview
	r1, _, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{
		State: ReviewStateComment,
		Body:  "I'll have a look at it later",
	})
	assert.NoError(t, err)
	if assert.NotNil(t, r1) {
		assert.EqualValues(t, ReviewStateComment, r1.State)
		assert.EqualValues(t, 1, r1.Reviewer.ID)
	}

	c.SetSudo(submitter.UserName)
	_, _, err = c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{
		State: ReviewStateApproved,
		Body:  "lgtm it myself",
	})
	assert.Error(t, err)
	r2, _, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{
		State: ReviewStateComment,
		Body:  "no seriously please have a look at it",
	})
	assert.NoError(t, err)
	assert.NotNil(t, r2)

	c.SetSudo(reviewer.UserName)
	r3, _, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{
		State: ReviewStateApproved,
		Body:  "lgtm",
		Comments: []CreatePullReviewComment{
			{
				Path:       "WOW-file",
				Body:       "no better name - really?",
				NewLineNum: 1,
			},
		},
	})
	assert.NoError(t, err)
	assert.NotNil(t, r3)

	// ListPullReviews
	c.SetSudo("")
	rl, _, err := c.ListPullReviews(repo.Owner.UserName, repo.Name, pull.Index, ListPullReviewsOptions{})
	assert.NoError(t, err)
	assert.Len(t, rl, 3)
	for i := range rl {
		assert.EqualValues(t, pull.HTMLURL, rl[i].HTMLPullURL)
		if rl[i].CodeCommentsCount == 1 {
			assert.EqualValues(t, reviewer.ID, rl[i].Reviewer.ID)
		}
	}

	// GetPullReview
	rNew, _, err := c.GetPullReview(repo.Owner.UserName, repo.Name, pull.Index, r3.ID)
	assert.NoError(t, err)
	assert.EqualValues(t, r3, rNew)

	// DeletePullReview
	c.SetSudo(submitter.UserName)
	_, err = c.DeletePullReview(repo.Owner.UserName, repo.Name, pull.Index, r2.ID)
	assert.NoError(t, err)
	_, err = c.DeletePullReview(repo.Owner.UserName, repo.Name, pull.Index, r3.ID)
	assert.Error(t, err)

	// SubmitPullReview
	c.SetSudo("")
	r4, resp, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{
		Body: "...",
		Comments: []CreatePullReviewComment{
			{
				Path:       "WOW-file",
				Body:       "its ok",
				NewLineNum: 1,
			},
		},
	})
	assert.NoError(t, err)
	assert.NotNil(t, resp)
	r5, _, err := c.CreatePullReview(repo.Owner.UserName, repo.Name, pull.Index, CreatePullReviewOptions{
		Body: "...",
		Comments: []CreatePullReviewComment{
			{
				Path:       "WOW-file",
				Body:       "hehe and here it is",
				NewLineNum: 3,
			},
		},
	})
	assert.NoError(t, err)
	assert.EqualValues(t, r4.ID, r5.ID)

	r, _, err := c.SubmitPullReview(repo.Owner.UserName, repo.Name, pull.Index, r4.ID, SubmitPullReviewOptions{
		State: ReviewStateRequestChanges,
		Body:  "one nit",
	})
	assert.NoError(t, err)
	assert.EqualValues(t, r4.ID, r.ID)
	assert.EqualValues(t, ReviewStateRequestChanges, r.State)

	// ListPullReviewComments
	rcl, _, err := c.ListPullReviewComments(repo.Owner.UserName, repo.Name, pull.Index, r.ID)
	assert.NoError(t, err)
	assert.EqualValues(t, r.CodeCommentsCount, len(rcl))
	for _, rc := range rcl {
		assert.EqualValues(t, pull.HTMLURL, rc.HTMLPullURL)
		if rc.LineNum == 3 {
			assert.EqualValues(t, "hehe and here it is", rc.Body)
		} else {
			assert.EqualValues(t, 1, rc.LineNum)
			assert.EqualValues(t, "its ok", rc.Body)
		}
	}

	r, _, err = c.GetPullReview(repo.Owner.UserName, repo.Name, pull.Index, r.ID)
	assert.NoError(t, err)
	assert.False(t, r.Dismissed)

	// DismissPullReview
	resp, err = c.DismissPullReview(repo.Owner.UserName, repo.Name, pull.Index, r.ID, DismissPullReviewOptions{Message: "stale"})
	assert.NoError(t, err)
	if assert.NotNil(t, resp) {
		assert.EqualValues(t, 200, resp.StatusCode)
	}
	r, _, _ = c.GetPullReview(repo.Owner.UserName, repo.Name, pull.Index, r.ID)
	assert.True(t, r.Dismissed)

	// UnDismissPullReview
	resp, err = c.UnDismissPullReview(repo.Owner.UserName, repo.Name, pull.Index, r.ID)
	assert.NoError(t, err)
	if assert.NotNil(t, resp) {
		assert.EqualValues(t, 200, resp.StatusCode)
	}
	r, _, _ = c.GetPullReview(repo.Owner.UserName, repo.Name, pull.Index, r.ID)
	assert.False(t, r.Dismissed)

	rl, _, err = c.ListPullReviews(repo.Owner.UserName, repo.Name, pull.Index, ListPullReviewsOptions{})
	assert.NoError(t, err)
	assert.Len(t, rl, 3)

	c.SetSudo(submitter.UserName)
	resp, err = c.CreateReviewRequests(repo.Owner.UserName, repo.Name, pull.Index, PullReviewRequestOptions{Reviewers: []string{reviewer.UserName}})
	assert.NoError(t, err)
	assert.NotNil(t, resp)

	rl, _, _ = c.ListPullReviews(repo.Owner.UserName, repo.Name, pull.Index, ListPullReviewsOptions{})
	if assert.Len(t, rl, 4) {
		assert.EqualValues(t, ReviewStateRequestReview, rl[3].State)
	}

	c.SetSudo(reviewer.UserName)
	resp, err = c.DeleteReviewRequests(repo.Owner.UserName, repo.Name, pull.Index, PullReviewRequestOptions{Reviewers: []string{reviewer.UserName}})
	assert.NoError(t, err)
	assert.NotNil(t, resp)

	rl, _, _ = c.ListPullReviews(repo.Owner.UserName, repo.Name, pull.Index, ListPullReviewsOptions{})
	assert.Len(t, rl, 3)

	c.SetSudo("")
	_, err = c.AdminDeleteUser(reviewer.UserName)
	assert.NoError(t, err)
	_, err = c.AdminDeleteUser(submitter.UserName)
	assert.NoError(t, err)
}

func preparePullReviewTest(t *testing.T, c *Client, repoName string) (*Repository, *PullRequest, *User, *User, bool) {
	repo, err := createTestRepo(t, repoName, c)
	if !assert.NoError(t, err) {
		return nil, nil, nil, nil, false
	}

	pullSubmitter := createTestUser(t, "pull_submitter", c)
	write := AccessModeWrite
	_, err = c.AddCollaborator(repo.Owner.UserName, repo.Name, pullSubmitter.UserName, AddCollaboratorOption{
		Permission: &write,
	})
	assert.NoError(t, err)

	c.SetSudo("pull_submitter")

	newFile, _, err := c.CreateFile(repo.Owner.UserName, repo.Name, "WOW-file", CreateFileOptions{
		Content: "QSBuZXcgRmlsZQoKYW5kIHNvbWUgbGluZXMK",
		FileOptions: FileOptions{
			Message:       "creat a new file",
			BranchName:    "main",
			NewBranchName: "new_file",
		},
	})

	if !assert.NoError(t, err) || !assert.NotNil(t, newFile) {
		return nil, nil, nil, nil, false
	}

	pull, _, err := c.CreatePullRequest(c.username, repoName, CreatePullRequestOption{
		Base:  "main",
		Head:  "new_file",
		Title: "Creat a NewFile",
	})
	assert.NoError(t, err)
	assert.NotNil(t, pull)

	c.SetSudo("")

	reviewer := createTestUser(t, "pull_reviewer", c)
	admin := AccessModeAdmin
	_, err = c.AddCollaborator(repo.Owner.UserName, repo.Name, pullSubmitter.UserName, AddCollaboratorOption{
		Permission: &admin,
	})
	assert.NoError(t, err)

	return repo, pull, pullSubmitter, reviewer, pull.Poster.ID == pullSubmitter.ID
}
