package main

import (
	"bytes"
	"errors"
	"log"
	"os"
	"path"
	"strings"
	"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 TestSyncPR(t *testing.T) {
	pr := PullRequestSynced{}

	config := &common.AutogitConfig{
		Reviewers:      []string{"reviewer1", "reviewer2"},
		Branch:         "testing",
		Organization:   "test",
		GitProjectName: "prj",
	}

	event := &common.PullRequestWebhookEvent{
		Action: "syncronized",
		Number: 42,
		Pull_Request: &common.PullRequest{
			Number: 42,
			Base: common.Head{
				Ref: "branch",
				Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
				Repo: &common.Repository{
					Name: "testRepo",
					Owner: &common.Organization{
						Username: config.Organization,
					},
					Default_Branch: "main1",
				},
			},
			Head: common.Head{
				Ref: "branch",
				Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
				Repo: &common.Repository{
					Name:           "testRepo",
					Default_Branch: "main1",
				},
			},
		},
		Repository: &common.Repository{
			Owner: &common.Organization{
				Username: config.Organization,
			},
		},
	}

	modelPR := &models.PullRequest{
		Index: 42,
		Body:  "PR: test/prj#24",
		Base: &models.PRBranchInfo{
			Ref: "branch",
			Sha: "8a6a69a4232cabda04a4d9563030aa888ff5482f75aa4c6519da32a951a072e2",
			Repo: &models.Repository{
				Name: "testRepo",
				Owner: &models.User{
					UserName: "test",
				},
				DefaultBranch: "main1",
			},
		},
		Head: &models.PRBranchInfo{
			Ref: "branch",
			Sha: "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985",
			Repo: &models.Repository{
				Name: "testRepo",
				Owner: &models.User{
					UserName: "test",
				},
				DefaultBranch: "main1",
			},
		},
	}

	PrjGitPR := &models.PullRequest{
		Title: "some pull request",
		Body:  "PR: test/testRepo#42",
		Index: 24,
		Head: &models.PRBranchInfo{
			Name: "testing",
			Sha:  "db8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95",
			Repo: &models.Repository{
				SSHURL: "./prj",
			},
		},
	}

	git := &common.GitHandlerImpl{
		GitCommiter: "tester",
		GitEmail:    "test@suse.com",
	}

	t.Run("PR sync request against PrjGit == no action", func(t *testing.T) {
		ctl := gomock.NewController(t)
		pr.gitea = mock_common.NewMockGitea(ctl)

		git.GitPath = t.TempDir()

		config.GitProjectName = "testRepo"
		event.Repository.Name = "testRepo"

		if err := pr.Process(event, git, config); err != nil {
			t.Error("Error PrjGit sync request. Should be no error.", err)
		}
	})

	t.Run("Missing submodule in prjgit", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mock := mock_common.NewMockGitea(ctl)

		pr.gitea = mock
		git.GitPath = t.TempDir()

		config.GitProjectName = "prjGit"
		event.Repository.Name = "testRepo"

		setupGitForTests(t, git)

		oldSha := PrjGitPR.Head.Sha
		defer func() { PrjGitPR.Head.Sha = oldSha }()
		PrjGitPR.Head.Sha = "ab8adab91edb476b9762097d10c6379aa71efd6b60933a1c0e355ddacf419a95"

		mock.EXPECT().GetPullRequest(config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
		mock.EXPECT().GetPullRequest(config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)

		err := pr.Process(event, git, config)

		if err == nil || err.Error() != "Cannot fetch submodule commit id in prjgit for 'testRepo'" {
			t.Error("Invalid error received.", err)
		}
	})

	t.Run("Missing PrjGit PR for the sync", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mock := mock_common.NewMockGitea(ctl)

		pr.gitea = mock
		git.GitPath = t.TempDir()

		config.GitProjectName = "prjGit"
		event.Repository.Name = "tester"

		setupGitForTests(t, git)

		expectedErr := errors.New("Missing PR should throw error")
		mock.EXPECT().GetPullRequest(config.Organization, "tester", event.Pull_Request.Number).Return(modelPR, expectedErr)

		err := pr.Process(event, git, config)

		if err == nil || errors.Unwrap(err) != expectedErr {
			t.Error("Invalid error received.", err)
		}
	})

	t.Run("PR sync", func(t *testing.T) {
		var b bytes.Buffer
		w := log.Writer()
		log.SetOutput(&b)
		defer log.SetOutput(w)

		ctl := gomock.NewController(t)
		mock := mock_common.NewMockGitea(ctl)

		pr.gitea = mock
		git.GitPath = t.TempDir()

		config.GitProjectName = "prjGit"
		event.Repository.Name = "testRepo"

		setupGitForTests(t, git)

		git.DebugLogger = true
		DebugMode = true
		//	mock.EXPECT().GetAssociatedPrjGitPR(event).Return(PrjGitPR, nil)
		mock.EXPECT().GetPullRequest(config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
		mock.EXPECT().GetPullRequest(config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)

		err := pr.Process(event, git, config)

		if err != nil {
			t.Error("Invalid error received.", err)
			t.Error(b.String())
		}

		// check that we actually created the branch in the prjgit
		id, ok := git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
		if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
			t.Error("Failed creating PR")
			t.Error(b.String())
		}

		/*
		 * does nothing on next sync of already synced data -- PR is updated
		 */
		os.RemoveAll(path.Join(git.GitPath, common.DefaultGitPrj))

		mock.EXPECT().GetPullRequest(config.Organization, "testRepo", event.Pull_Request.Number).Return(modelPR, nil)
		mock.EXPECT().GetPullRequest(config.Organization, "prj", int64(24)).Return(PrjGitPR, nil)
		err = pr.Process(event, git, config)

		if err != nil {
			t.Error("Invalid error received.", err)
			t.Error(b.String())
		}

		// check that we actually created the branch in the prjgit
		id, ok = git.GitSubmoduleCommitId("prj", "testRepo", "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c")
		if id != "11eb36d5a58d7bb376cac59ac729a1986c6a7bfc63e7818e14382f545ccda985" || !ok {
			t.Error("Failed creating PR")
			t.Error(b.String())
		}

		if id, err := git.GitBranchHead("prj", "PR_testRepo#42"); id != "c097b9d1d69892d0ef2afa66d4e8abf0a1612c6f95d271a6e15d6aff1ad2854c" || err != nil {
			t.Error("no branch?", err)
			t.Error(b.String())
		}

		if !strings.Contains(b.String(), "commitID already match - nothing to do") {
			// os.CopyFS("/tmp/test", os.DirFS(git.GitPath))
			t.Log(b.String())
		}
	})
}
