package operate

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
	v1 "github.com/tidbcloud/infra-component/api/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestGenerateComponentSets(t *testing.T) {
	alphaSpec := v1.ComponentSpec{Version: "v1.0.0"}
	betaSpec := v1.ComponentSpec{Version: "v2.0.0"}
	alphaComponent := v1.Component{
		ObjectMeta: metav1.ObjectMeta{
			Name: "component",
		},
		Spec: alphaSpec,
	}
	betaComponent := v1.Component{
		ObjectMeta: metav1.ObjectMeta{
			Name: "component",
		},
		Spec: betaSpec,
	}

	testCases := []struct {
		name              string
		componentSets     []v1.ComponentSet
		components        []v1.Component
		wantComponentSets []v1.ComponentSet
	}{
		{
			name: "empty components and componentsets",
		},
		{
			name:          "empty componentsets and new component",
			componentSets: nil,
			components:    []v1.Component{alphaComponent},
			wantComponentSets: []v1.ComponentSet{
				{
					ObjectMeta: metav1.ObjectMeta{Name: "component"},
					Spec: v1.ComponentSetSpec{
						Versions: []v1.ComponentSpec{alphaSpec},
					},
				},
			},
		},
		{
			name: "unchanged component",
			componentSets: []v1.ComponentSet{
				{
					ObjectMeta: metav1.ObjectMeta{Name: "component"},
					Spec: v1.ComponentSetSpec{
						Versions: []v1.ComponentSpec{alphaSpec},
					},
				},
			},
			components: []v1.Component{alphaComponent},
			wantComponentSets: []v1.ComponentSet{
				{
					ObjectMeta: metav1.ObjectMeta{Name: "component"},
					Spec: v1.ComponentSetSpec{
						Versions: []v1.ComponentSpec{alphaSpec},
					},
				},
			},
		},
		{
			name: "new component",
			componentSets: []v1.ComponentSet{
				{
					ObjectMeta: metav1.ObjectMeta{Name: "component"},
					Spec: v1.ComponentSetSpec{
						Versions: []v1.ComponentSpec{alphaSpec},
					},
				},
			},
			components: []v1.Component{alphaComponent, betaComponent},
			wantComponentSets: []v1.ComponentSet{
				{
					ObjectMeta: metav1.ObjectMeta{Name: "component"},
					Spec: v1.ComponentSetSpec{
						Versions: []v1.ComponentSpec{alphaSpec, betaSpec},
					},
				},
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			componentDir, err := ioutil.TempDir(".", "component")
			defer os.RemoveAll(componentDir)
			if err != nil {
				t.Fatal(err)
			}
			if err = writeComponentDir(componentDir, tc.components); err != nil {
				t.Fatal(err)
			}
			componentSetDir, err := ioutil.TempDir(".", "componentset")
			defer os.RemoveAll(componentSetDir)
			if err != nil {
				t.Fatal(err)
			}
			if err = writeComponentSetDir(componentSetDir, tc.componentSets); err != nil {
				t.Fatal(err)
			}
			err = GenerateComponentSets(componentDir, componentSetDir)
			if err != nil {
				t.Fatalf("GenerateComponentSets got error = %v, want no error", err)
			}
			gotComponentSets, err := readComponentSetDir(componentSetDir)
			if err != nil {
				t.Fatal(err)
			}
			trans := cmp.Transformer("", func(in []v1.ComponentSet) []v1.ComponentSet {
				m := make(map[string]v1.ComponentSet)
				for _, componentset := range in {
					m[componentset.Name] = componentset
				}
				var out []v1.ComponentSet
				for _, componentset := range m {
					out = append(out, componentset)
				}
				return out
			})
			if diff := cmp.Diff(gotComponentSets, tc.wantComponentSets, trans); diff != "" {
				t.Fatalf("GenerateComponentSets diff = %s", diff)
			}
		})
	}
}
