package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

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

func TestProjectBranchName(t *testing.T) {
	req := common.PullRequestWebhookEvent{
		Repository: &common.Repository{
			Name: "testingRepo",
		},
		Pull_Request: &common.PullRequest{
			Number: 10,
		},
	}

	branchName := prGitBranchNameForPR(&req)
	if branchName != "PR_testingRepo#10" {
		t.Error("Unexpected branch name:", branchName)
	}
}

func TestProjctGitSync(t *testing.T) {
	req := common.PullRequestWebhookEvent{
		Action: "pull",
		Number: 0,
	}

	if err := processPrjGitPullRequestSync(&req); err != nil {
		t.Error(err)
	}
}

const LocalCMD = "---"

func gitExecs(t *testing.T, git *common.GitHandlerImpl, cmds [][]string) {
	for _, cmd := range cmds {
		if cmd[0] == LocalCMD {
			command := exec.Command(cmd[2], cmd[3:]...)
			command.Dir = filepath.Join(git.GitPath, cmd[1])
			command.Stdin = nil
			command.Env = append([]string{"GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_1=protocol.file.allow", "GIT_CONFIG_VALUE_1=always"}, common.ExtraGitParams...)
			_, err := command.CombinedOutput()
			if err != nil {
				t.Errorf(" *** error: %v\n", err)
			}
		} else {
			git.GitExecOrPanic(cmd[0], cmd[1:]...)
		}
	}
}

func commandsForPackages(dir, prefix string, startN, endN int) [][]string {
	commands := make([][]string, (endN-startN+2)*6)

	if dir == "" {
		dir = "."
	}
	cmdIdx := 0
	for idx := startN; idx <= endN; idx++ {
		pkgDir := fmt.Sprintf("%s%d", prefix, idx)

		commands[cmdIdx+0] = []string{"", "init", "-q", "--object-format", "sha256", "-b", "testing", pkgDir}
		commands[cmdIdx+1] = []string{LocalCMD, pkgDir, "/usr/bin/touch", "testFile"}
		commands[cmdIdx+2] = []string{pkgDir, "add", "testFile"}
		commands[cmdIdx+3] = []string{pkgDir, "commit", "-m", "added testFile"}
		commands[cmdIdx+4] = []string{pkgDir, "config", "receive.denyCurrentBranch", "ignore"}
		commands[cmdIdx+5] = []string{"prj", "submodule", "add", filepath.Join("..", pkgDir), filepath.Join(dir, pkgDir)}

		cmdIdx += 6
	}

	// add all the submodules to the prj
	commands[cmdIdx+0] = []string{"prj", "commit", "-a", "-m", "adding subpackages"}

	return commands
}

func setupGitForTests(t *testing.T, git *common.GitHandlerImpl) {
	common.ExtraGitParams = []string{
		"GIT_CONFIG_COUNT=1",
		"GIT_CONFIG_KEY_0=protocol.file.allow",
		"GIT_CONFIG_VALUE_0=always",

		"GIT_AUTHOR_NAME=testname",
		"GIT_AUTHOR_EMAIL=test@suse.com",
		"GIT_AUTHOR_DATE='2005-04-07T22:13:13'",
		"GIT_COMMITTER_NAME=testname",
		"GIT_COMMITTER_EMAIL=test@suse.com",
		"GIT_COMMITTER_DATE='2005-04-07T22:13:13'",
	}

	gitExecs(t, git, [][]string{
		{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "prj"},
		{"", "init", "-q", "--object-format", "sha256", "-b", "testing", "foo"},
		{LocalCMD, "foo", "/usr/bin/touch", "file1"},
		{"foo", "add", "file1"},
		{"foo", "commit", "-m", "first commit"},
		{"prj", "config", "receive.denyCurrentBranch", "ignore"},
		{"prj", "submodule", "init"},
		{"prj", "submodule", "add", "../foo", "testRepo"},
		{"prj", "add", ".gitmodules", "testRepo"},
		{"prj", "commit", "-m", "First instance"},
		{"prj", "submodule", "deinit", "testRepo"},
		{LocalCMD, "foo", "/usr/bin/touch", "file2"},
		{"foo", "add", "file2"},
		{"foo", "commit", "-m", "added file2"},
	})
}

func TestUpdatePrBranch(t *testing.T) {
	var buf bytes.Buffer
	origLogger := log.Writer()
	log.SetOutput(&buf)
	defer log.SetOutput(origLogger)

	req := &common.PullRequestWebhookEvent{
		Repository: &common.Repository{
			Name: "testRepo",
		},
		Pull_Request: &common.PullRequest{},
	}

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

	setupGitForTests(t, git)
	gitExecs(t, git, [][]string{{"", "clone", "prj", common.DefaultGitPrj}})

	revs := strings.Split(git.GitExecWithOutputOrPanic("foo", "rev-list", "HEAD"), "\n")
	req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
	req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])

	updateSubmoduleInPR(req, git)
	common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
	common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:+testing"))
	git.GitExecOrPanic("prj", "reset", "--hard", "testing")
	rev := strings.TrimSpace(git.GitExecWithOutputOrPanic(filepath.Join(common.DefaultGitPrj, "testRepo"), "rev-list", "-1", "HEAD"))
	if rev != req.Pull_Request.Head.Sha {
		t.Error("prj/testRepo not updated to", req.Pull_Request.Head.Sha, "but is at", rev)
		t.Error(buf.String())
	}
}

func TestCreatePrBranch(t *testing.T) {
	var buf bytes.Buffer
	origLogger := log.Writer()
	log.SetOutput(&buf)
	defer log.SetOutput(origLogger)

	req := &common.PullRequestWebhookEvent{
		Repository: &common.Repository{
			Name: "testRepo",
		},
		Pull_Request: &common.PullRequest{},
	}

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

	setupGitForTests(t, git)
	gitExecs(t, git, [][]string{{"", "clone", "prj", common.DefaultGitPrj}})

	revs := strings.Split(git.GitExecWithOutputOrPanic("foo", "rev-list", "HEAD"), "\n")
	req.Pull_Request.Base.Sha = strings.TrimSpace(revs[1])
	req.Pull_Request.Head.Sha = strings.TrimSpace(revs[0])

	updateSubmoduleInPR(req, git)
	common.PanicOnError(git.GitExec(common.DefaultGitPrj, "commit", "-a", "-m", "created commit"))
	common.PanicOnError(git.GitExec(common.DefaultGitPrj, "push", "origin", "+HEAD:testingCreated"))

	rev := strings.TrimSpace(git.GitExecWithOutputOrPanic(filepath.Join(common.DefaultGitPrj, "testRepo"), "rev-list", "-1", "HEAD"))
	if rev != req.Pull_Request.Head.Sha {
		t.Error("prj/testRepo not updated to", req.Pull_Request.Head.Sha, "but is at", rev)
		t.Error(buf.String())
	}

		 os.CopyFS("/tmp/test", os.DirFS(git.GitPath))
	git.GitExecOrPanic("prj", "reset", "--hard", "testingCreated")
	rev = strings.TrimSpace(git.GitExecWithOutputOrPanic("prj", "submodule", "status", "testRepo"))[1 : len(req.Pull_Request.Head.Sha)+1]
	if rev != req.Pull_Request.Head.Sha {
		t.Error("prj/testRepo not updated to", req.Pull_Request.Head.Sha, "but is at", rev)
		t.Error(buf.String())
	}
}
