package common_test

import (
	"errors"
	"testing"

	"go.uber.org/mock/gomock"
	"src.opensuse.org/autogits/common"
	"src.opensuse.org/autogits/common/gitea-generated/models"
	mock_common "src.opensuse.org/autogits/common/mock"
)

func TestReviews(t *testing.T) {
	tests := []struct {
		name              string
		reviews           []*models.PullReview
		reviewers         []string
		fetchErr          error
		isApproved        bool
		isReviewedByTest1 bool
		isPendingByTest1  bool
	}{
		{
			name:       "Reviews of unreviews PR",
			isApproved: false,
		},
		{
			name:       "Single reviewer done",
			reviews:    []*models.PullReview{&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}}},
			reviewers:  []string{"user1"},
			isApproved: true,
			isReviewedByTest1: true,
		},
		{
			name:       "Two reviewer, one not approved",
			reviews:    []*models.PullReview{&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}}},
			reviewers:  []string{"user1", "user2"},
			isApproved: false,
			isReviewedByTest1: true,
		},
		{
			name: "Two reviewer, one stale approved",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}, Stale: true},
			},
			reviewers:  []string{"user1", "user2"},
			isApproved: false,
			isReviewedByTest1: true,
		},
		{
			name: "Two reviewer, one is pending",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateRequestReview, User: &models.User{UserName: "user1"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
			},
			reviewers:  []string{"user1", "user2"},
			isApproved: false,
			isPendingByTest1: true,
		},
		{
			name: "Two reviewer, one stale and pending",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateRequestReview, User: &models.User{UserName: "user1"}, Stale: true},
			},
			reviewers:  []string{"user1", "user2"},
			isApproved: false,
			isPendingByTest1: false,
			isReviewedByTest1: false,
		},
		{
			name: "Two reviewer approved",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
			},
			reviewers:  []string{"user1", "user2"},
			isApproved: true,
			isReviewedByTest1: true,
		},
		{
			name: "Two reviewer approved, one is dismissed",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}, Dismissed: true},
			},
			reviewers:  []string{"user1", "user2"},
			isApproved: false,
			isReviewedByTest1: true,
		},
		{
			name: "Two reviewer approved, but fetch error",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
			},
			reviewers:  []string{"user1", "user2"},
			fetchErr:   errors.New("System error fetching reviews."),
			isApproved: true,
			isReviewedByTest1: true,
		},
		{
			name: "Extra reviewers are ignored",
			reviews: []*models.PullReview{
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user1"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user4"}},
				&models.PullReview{State: common.ReviewStateApproved, User: &models.User{UserName: "user2"}},
			},
			reviewers:  []string{"user1", "user2"},
			isApproved: true,
			isReviewedByTest1: true,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			ctl := gomock.NewController(t)
			rf := mock_common.NewMockGiteaReviewFetcher(ctl)

			rf.EXPECT().GetPullRequestReviews("test", "pr", int64(1)).Return(test.reviews, test.fetchErr)

			reviews, err := common.FetchGiteaReviews(rf, test.reviewers, "test", "pr", 1)

			if test.fetchErr != nil {
				if err != test.fetchErr {
					t.Fatal("FetchReviews() failed with unexpected error:", err)
				}
				return
			}

			if r := reviews.IsApproved(); r != test.isApproved {
				t.Fatal("Unexpected IsReviewed():", r, "vs. expected", test.isApproved)
			}

			if r := reviews.HasPendingReviewBy("user1"); r != test.isPendingByTest1 {
				t.Fatal("Unexpected IsReviewPendingBy(user1):", r)
			}
			if r := reviews.IsReviewedBy("user1"); r != test.isReviewedByTest1 {
				t.Fatal("Unexpected IsReviewedBy(user1):", r)
			}

			if r := reviews.HasPendingReviewBy("random"); r {
				t.Fatal("Unexpected IsReviewPendingBy(random):", r)
			}
			if r := reviews.IsReviewedBy("random"); r {
				t.Fatal("Unexpected IsReviewedBy(random):", r)
			}
		})
	}
}
