package main

import (
	"bytes"
	"fmt"
	"log"
	"testing"

	"go.uber.org/mock/gomock"
	"src.opensuse.org/autogits/common"

	mock_common "src.opensuse.org/autogits/common/mock"
	mock_main "src.opensuse.org/workflow-pr/mock"
)

func TestPRProcessor(t *testing.T) {
	tests := []struct {
		title  string
		action string
		req    func(req *RequestProcessor, mock PullRequestProcessor)
	}{
		{
			title:  "Open routine called for PR opening",
			action: "opened",
			req: func(req *RequestProcessor, mock PullRequestProcessor) {
				req.Opened = mock
			},
		},
		{
			title:  "Re-Open routine called for PR reopening",
			action: "reopened",
			req: func(req *RequestProcessor, mock PullRequestProcessor) {
				req.Opened = mock
			},
		},
		{
			title:  "Sync routine called for PR sync requests",
			action: "synchronized",
			req: func(req *RequestProcessor, mock PullRequestProcessor) {
				req.Synced = mock
			},
		},
		{
			title:  "Close routine called for PR closing",
			action: "closed",
			req: func(req *RequestProcessor, mock PullRequestProcessor) {
				req.Closed = mock
			},
		},
		{
			title:  "Close routine called for PR closing",
			action: "reviewed",
			req: func(req *RequestProcessor, mock PullRequestProcessor) {
				req.Review = mock
			},
		},
	}

	var logBuf bytes.Buffer
	oldOut := log.Writer()
	log.SetOutput(&logBuf)
	defer log.SetOutput(oldOut)

	testConfiguration := make(map[string][]*common.AutogitConfig)

	testConfiguration["test"] = make([]*common.AutogitConfig, 1, 1)
	testConfiguration["test"][0] = &common.AutogitConfig{
		Branch: "branch",
	}

	event := &common.PullRequestWebhookEvent{
		// Action: "opened",
		Number: 1,
		Pull_Request: &common.PullRequest{
			Id: 1,
			Base: common.Head{
				Ref: "branch",
				Repo: &common.Repository{
					Name: "testRepo",
				},
			},
			Head: common.Head{
				Ref: "branch",
				Repo: &common.Repository{
					Name: "testRepo",
				},
			},
		},
		Repository: &common.Repository{
			Owner: &common.Organization{
				Username: "test",
			},
		},
	}

	for _, test := range tests {
		t.Run(test.title, func(t *testing.T) {
			ctl := gomock.NewController(t)
			mock := mock_main.NewMockPullRequestProcessor(ctl)
			mock.EXPECT().Process(event, gomock.Any(), testConfiguration["test"][0]).Return(nil)

			req := &RequestProcessor{
				configuredRepos: testConfiguration,
				git:             &common.GitHandlerGeneratorImpl{},
			}
			test.req(req, mock)

			event.Action = test.action

			err := req.ProcessFunc(&common.Request{
				Data: event,
			})

			if err != nil {
				t.Error("Error processing open PR:", err)
				t.Error(logBuf.String())
			}
		})
	}

	req := &RequestProcessor{
		configuredRepos: testConfiguration,
		git:             &common.GitHandlerGeneratorImpl{},
	}

	t.Run("Edit PR handling", func(t *testing.T) {
		/*		ctl := gomock.NewController(t)
				closedMock := mock_main.NewMockPullRequestProcessor(ctl)
				closedMock.EXPECT().Process(event, gomock.Any(), testConfiguration["test"][0]).Return(nil)
		*/

		//		req.Closed = closedMock
		event.Action = "edited"

		err := req.ProcessFunc(&common.Request{
			Data: event,
		})

		if err != nil {
			t.Error("Error processing edit PR:", err)
			t.Error(logBuf.String())
		}
	})

	t.Run("Unknown PR-type handling", func(t *testing.T) {
		event.Action = "not existing action"

		err := req.ProcessFunc(&common.Request{
			Data: event,
		})

		if err == nil {
			t.Error(logBuf.String())
		}
	})

	t.Run("Missing branch in config present in PR", func(t *testing.T) {
		baseRef := event.Pull_Request.Base.Ref
		event.Pull_Request.Base.Ref = "not present"
		err := req.ProcessFunc(&common.Request{
			Data: event,
		})
		event.Pull_Request.Base.Ref = baseRef

		if err == nil {
			t.Error(logBuf.String())
		}
	})

	t.Run("Invalid data present in PR", func(t *testing.T) {
		baseConfig := req.configuredRepos
		req.configuredRepos = make(map[string][]*common.AutogitConfig)
		err := req.ProcessFunc(&common.Request{
			Data: nil,
		})
		req.configuredRepos = baseConfig

		if err == nil {
			t.Error(logBuf.String())
		}
	})

	t.Run("Ignoring requests against unconfigured repos", func(t *testing.T) {
		baseConfig := req.configuredRepos
		req.configuredRepos = make(map[string][]*common.AutogitConfig)
		err := req.ProcessFunc(&common.Request{
			Data: event,
		})
		req.configuredRepos = baseConfig

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

	t.Run("Failures of git handler creation", func(t *testing.T) {
		ctl := gomock.NewController(t)
		gitHandler := mock_common.NewMockGitHandlerGenerator(ctl)

		gitHandler.EXPECT().CreateGitHandler(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("some error"))

		origHandler := req.git
		req.git = gitHandler

		err := req.ProcessFunc(&common.Request{
			Data: event,
		})

		req.git = origHandler

		if err == nil {
			t.Error(logBuf.String())
		}
	})
}
