package repository

import (
	"fmt"
	"strings"
	"testing"

	apipac "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
	"github.com/openshift-pipelines/pipelines-as-code/pkg/cli"
	"github.com/openshift-pipelines/pipelines-as-code/pkg/cli/prompt"
	"github.com/openshift-pipelines/pipelines-as-code/pkg/git"
	"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/clients"
	"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
	testclient "github.com/openshift-pipelines/pipelines-as-code/pkg/test/clients"
	"gotest.tools/v3/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	rtesting "knative.dev/pkg/reconciler/testing"
)

func TestGetRepoURL(t *testing.T) {
	tests := []struct {
		name       string
		wantErrStr string
		askStubs   func(*prompt.AskStubber)
		tdata      testclient.Data
		runInfo    info.Info
		gitinfo    git.Info
		repo       apipac.Repository
		wantStdout string
		event      info.Event
		wantURL    string
	}{
		{
			name:       "URL already set",
			wantStdout: "",
			event: info.Event{
				URL: "http://knockknock",
			},
			wantURL: "http://knockknock",
		},
		{
			name: "default to gitinfo",
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("") // use default
			},
			gitinfo: git.Info{
				URL: "https://url/tartanpion",
			},
			wantURL: "https://url/tartanpion",
		},
		{
			name: "set from question",
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("https://url/tartanpion")
			},
			wantURL: "https://url/tartanpion",
		},
		{
			name: "no url has been provided",
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("")
			},
			wantErrStr: "no url has been provided",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx, _ := rtesting.SetupFakeContext(t)
			stdata, _ := testclient.SeedTestData(t, ctx, tt.tdata)

			as, teardown := prompt.InitAskStubber()
			defer teardown()
			if tt.askStubs != nil {
				tt.askStubs(as)
			}
			io, _, _, _ := cli.IOTest()
			err := getRepoURL(&createOptions{
				event:      &tt.event,
				repository: &tt.repo,
				gitInfo:    &tt.gitinfo,
				ioStreams:  io,
				cliOpts:    &cli.PacCliOpts{},
				run: &params.Run{
					Clients: clients.Clients{
						Kube: stdata.Kube,
					},
					Info: tt.runInfo,
				},
			})
			assert.Equal(t, tt.wantURL, tt.event.URL)

			if tt.wantErrStr != "" {
				assert.Equal(t, err.Error(), tt.wantErrStr)
				return
			}

			assert.NilError(t, err)
		})
	}
}

func TestGetNamespace(t *testing.T) {
	nshere := "tartanpion"
	nsnothere := "fantomas"

	tests := []struct {
		name       string
		wantErrStr string
		askStubs   func(*prompt.AskStubber)
		tdata      testclient.Data
		runInfo    info.Info
		gitinfo    git.Info
		repo       apipac.Repository
		wantStdout string
	}{
		{
			name: "ns already set",
			repo: apipac.Repository{
				ObjectMeta: metav1.ObjectMeta{Namespace: nshere},
			},
			wantStdout: "",
		},
		{
			name:       "change default to current git basename and suffix pipelines",
			wantStdout: "! Namespace tartanpion-pipelines is not found",
			gitinfo: git.Info{
				URL: "https://url/tartanpion",
			},
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("") // use default
				as.StubOne(true)
			},
			runInfo: info.Info{
				Kube: info.KubeOpts{
					Namespace: "default",
				},
			},
		},
		{
			name: "error you need to create the namespace first",
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("pasenvie") // use default
				as.StubOne(false)
			},
			wantStdout: "! Namespace pasenvie is not found",
			wantErrStr: "you need to create the target namespace first",
		},
		{
			name: "create ns here",
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("") // use default
			},
			tdata: testclient.Data{
				Namespaces: []*corev1.Namespace{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name: nshere,
						},
					},
				},
			},
			runInfo: info.Info{
				Kube: info.KubeOpts{
					Namespace: nshere,
				},
			},
			wantStdout: "",
		},
		{
			name: "create ns not here",
			askStubs: func(as *prompt.AskStubber) {
				as.StubOne("fantomas") // use default
				as.StubOne(true)
			},
			wantStdout: fmt.Sprintf("! Namespace %s is not found", nsnothere),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx, _ := rtesting.SetupFakeContext(t)
			stdata, _ := testclient.SeedTestData(t, ctx, tt.tdata)

			as, teardown := prompt.InitAskStubber()
			defer teardown()
			if tt.askStubs != nil {
				tt.askStubs(as)
			}
			io, _, stdout, _ := cli.IOTest()
			err := getOrCreateNamespace(ctx, &createOptions{
				event:      &info.Event{},
				repository: &tt.repo,
				gitInfo:    &tt.gitinfo,
				ioStreams:  io,
				cliOpts:    &cli.PacCliOpts{},
				run: &params.Run{
					Clients: clients.Clients{
						Kube: stdata.Kube,
					},
					Info: tt.runInfo,
				},
			})
			assert.Assert(t, strings.TrimSpace(stdout.String()) == strings.TrimSpace(tt.wantStdout),
				"\"%s\" not equal to \"%s\"", strings.TrimSpace(stdout.String()), tt.wantStdout)

			if tt.wantErrStr != "" {
				assert.Equal(t, err.Error(), tt.wantErrStr)
				return
			}

			assert.NilError(t, err)
		})
	}
}
