package main

import (
	"bytes"
	"errors"
	"log"
	"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"
	mock_main "src.opensuse.org/workflow-pr/mock"
)

func TestRepoCheck(t *testing.T) {
	var logBuf bytes.Buffer
	oldOut := log.Writer()
	log.SetOutput(&logBuf)
	defer log.SetOutput(oldOut)

	t.Run("Consistency Check On Start", func(t *testing.T) {
		c := CreateDefaultStateChecker(true, nil, nil, 100)
		ctl := gomock.NewController(t)
		state := mock_main.NewMockStateChecker(ctl)
		c.i = state
		state.EXPECT().CheckRepos().Do(func() error {
			// only checkOnStart has checkInterval = 0
			if c.checkInterval != 0 {
				t.Fail()
			}

			c.exitCheckLoop = true
			return nil
		})

		c.ConsistencyCheckProcess()
		if c.checkInterval != 100 {
			t.Fail()
		}
	})

	t.Run("No consistency Check On Start", func(t *testing.T) {
		c := CreateDefaultStateChecker(true, nil, nil, 100)
		ctl := gomock.NewController(t)
		state := mock_main.NewMockStateChecker(ctl)
		c.i = state

		nCalls := 10
		state.EXPECT().CheckRepos().Do(func() error {
			// only checkOnStart has checkInterval = 0
			if c.checkInterval != 100 {
				t.Fail()
			}

			nCalls--
			if nCalls == 0 {
				c.exitCheckLoop = true
			}
			return nil
		}).Times(nCalls)
		c.checkOnStart = false

		c.ConsistencyCheckProcess()
	})

	t.Run("CheckRepos() calls CheckProjectState() for each project", func(t *testing.T) {
		ctl := gomock.NewController(t)
		state := mock_main.NewMockStateChecker(ctl)
		gitea := mock_common.NewMockGitea(ctl)

		config1 := &common.AutogitConfig{
			GitProjectName: "git_repo1",
			Organization:   "repo1_org",
		}
		config2 := &common.AutogitConfig{
			GitProjectName: "git_repo2",
			Organization:   "repo2_org",
		}
		config3 := &common.AutogitConfig{
			GitProjectName: "git_repo3",
			Organization:   "repo3_org",
		}

		configs := &RequestProcessor{
			configuredRepos: map[string][]*common.AutogitConfig{
				"repo1_org": []*common.AutogitConfig{config1},
				"repo2_org": []*common.AutogitConfig{config2},
				"repo3_org": []*common.AutogitConfig{config3},
			},
		}
		r := configs.configuredRepos

		c := CreateDefaultStateChecker(true, configs, gitea, 100)
		c.i = state

		state.EXPECT().VerifyProjectState("repo1_org", r["repo1_org"], 0)
		state.EXPECT().VerifyProjectState("repo2_org", r["repo2_org"], 0)
		state.EXPECT().VerifyProjectState("repo3_org", r["repo3_org"], 0)

		if err := c.CheckRepos(); err != nil {
			t.Error(err)
		}
	})

	t.Run("CheckRepos errors", func(t *testing.T) {
		ctl := gomock.NewController(t)
		state := mock_main.NewMockStateChecker(ctl)
		gitea := mock_common.NewMockGitea(ctl)
		git := mock_common.NewMockGitHandlerGenerator(ctl)

		config1 := &common.AutogitConfig{
			GitProjectName: "git_repo1",
			Organization:   "repo1_org",
		}

		configs := &RequestProcessor{
			configuredRepos: map[string][]*common.AutogitConfig{
				"repo1_org": []*common.AutogitConfig{config1},
			},
		}
		//r := configs.configuredRepos

		c := CreateDefaultStateChecker(true, configs, gitea, 100)
		c.i = state
		c.git = git

		err := errors.New("test error")
		state.EXPECT().VerifyProjectState("repo1_org", gomock.Any(), 0).Return(err)

		r := c.CheckRepos()

		if !errors.Is(r, err) {
			t.Error(err)
		}
	})
}

type testGit struct {
	git *common.GitHandlerImpl
}

func (s *testGit) CreateGitHandler(a, b, c string) (common.Git, error) {
	return s.git, nil
}

func (s *testGit) ReadExistingPath(a,b,c string) (common.Git, error) {
	return nil, errors.New("should not be called")
}

func TestVerifyProjectState(t *testing.T) {
	var logBuf bytes.Buffer
	oldOut := log.Writer()
	log.SetOutput(&logBuf)
	defer log.SetOutput(oldOut)

	t.Run("Project state with no PRs", func(t *testing.T) {
		ctl := gomock.NewController(t)
		gitea := mock_common.NewMockGitea(ctl)

		git := &common.GitHandlerImpl{
			DebugLogger: true,
			GitCommiter: "TestCommiter",
			GitEmail:    "test@testing",
			GitPath:     t.TempDir(),
		}

		setupGitForTests(t, git)

		org := "repo1_org"
		config1 := &common.AutogitConfig{
			GitProjectName: "git_repo1",
			Organization:   "repo1_org",
			Branch:         "testing",
			Reviewers:      []string{"reviewer1", "reviewer2"},
			Workflows:      []string{"pr"},
		}
		configs := &RequestProcessor{
			configuredRepos: map[string][]*common.AutogitConfig{
				org: []*common.AutogitConfig{config1},
			},
		}

		gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
			SSHURL: "./prj",
		}, nil)
		gitea.EXPECT().GetRecentPullRequests(org, "testRepo")
		gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())

		c := CreateDefaultStateChecker(false, configs, gitea, 0)
		c.git = &testGit{
			git: git,
		}

		err := c.VerifyProjectState("repo1_org", configs.configuredRepos[org], 0)

		if err != nil {
			t.Error(err)
		}
	})

	t.Run("Project state with 1 PRs that doesn't trigger updates", func(t *testing.T) {
		ctl := gomock.NewController(t)
		gitea := mock_common.NewMockGitea(ctl)
		process := mock_main.NewMockPullRequestProcessor(ctl)

		git := &common.GitHandlerImpl{
			DebugLogger: true,
			GitCommiter: "TestCommiter",
			GitEmail:    "test@testing",
			GitPath:     t.TempDir(),
		}

		setupGitForTests(t, git)

		org := "repo1_org"
		config1 := &common.AutogitConfig{
			GitProjectName: "git_repo1",
			Organization:   "repo1_org",
			Branch:         "testing",
			Reviewers:      []string{"reviewer1", "reviewer2"},
			Workflows:      []string{"pr"},
		}
		configs := &RequestProcessor{
			configuredRepos: map[string][]*common.AutogitConfig{
				org: []*common.AutogitConfig{config1},
			},
		}

		gitea.EXPECT().CreateRepositoryIfNotExist(gomock.Any(), gomock.Any(), config1.GitProjectName).Return(&models.Repository{
			SSHURL: "./prj",
		}, nil)

		gitea.EXPECT().GetRecentPullRequests(org, "testRepo").Return([]*models.PullRequest{
			&models.PullRequest{
				ID:    1234,
				URL:   "url here",
				Index: 1234,
				State: "open",
				Labels: []*models.Label{
					&models.Label{
						ID: 1,
					},
				},
				User: &models.User{},

				Base: &models.PRBranchInfo {
					Name: "one",
					Ref: "main",
					Sha: "123",
					Repo: &models.Repository {
						Owner: &models.User {
						},
					},
				},
				Head: &models.PRBranchInfo {
					Name: "one",
					Ref: "main",
					Sha: "123",
					Repo: &models.Repository {
						Owner: &models.User {
						},
					},
				},
			},
		}, nil)

		gitea.EXPECT().GetRecentCommits(org, "testRepo", "testing", gomock.Any())

		c := CreateDefaultStateChecker(false, configs, gitea, 0)
		c.git = &testGit{
			git: git,
		}
		process.EXPECT().Process(gomock.Any(), gomock.Any(), gomock.Any())
		c.processor.Opened = process

		err := c.VerifyProjectState("repo1_org", configs.configuredRepos[org], 0)

		if err != nil {
			t.Error(err)
		}
	})
}
