package revision

import (
	"testing"

	. "github.com/onsi/gomega"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

func TestRevisioner_CreateConfigRevision(t *testing.T) {
	type subcase struct {
		name     string
		revision *cloudv1.ConfigRevision
		existing *cloudv1.ConfigRevision
		rehash   bool
	}

	g := NewGomegaWithT(t)
	scheme := runtime.NewScheme()
	g.Expect(cloudv1.AddToScheme(scheme)).To(Succeed())

	t1cc1 := &cloudv1.ClusterConfig{
		ObjectMeta: v1.ObjectMeta{
			Labels: map[string]string{
				cloudv1.VersionLabel: "v6.1.0",
				cloudv1.TenantTagKey: "1",
			},
		},
		Spec: cloudv1.ClusterConfigSpec{
			ConfigMeta: &cloudv1.ConfigMeta{
				GitCommitID:   "abcdefghijk",
				ConfigVersion: 0,
			},
			TiDB: &cloudv1.Configuration{
				Config: cloudv1.NewTomlConfig(map[string]interface{}{
					"token-limit": int64(1000),
				}),
			},
			TiKV: &cloudv1.Configuration{
				Config: cloudv1.NewTomlConfig(map[string]interface{}{
					"abort-on-panic": false,
				}),
			},
			PD: &cloudv1.Configuration{
				Config: cloudv1.NewTomlConfig(map[string]interface{}{}),
			},
			TiFlash: &cloudv1.Configuration{
				Config: cloudv1.NewTomlConfig(map[string]interface{}{}),
			},
		},
	}

	t1cc2 := t1cc1.DeepCopy()
	t1cc2.Spec.TiFlash = nil

	t1Rev1 := NewConfigRevision(t1cc1)
	t1Rev2 := NewConfigRevision(t1cc2)
	t1Rev2.Name = t1Rev1.Name
	t1Rev2.Labels[cloudv1.ConfigRevisionHashLabel] = t1Rev1.Labels[cloudv1.ConfigRevisionHashLabel]

	testFn := func(tc subcase) {
		clone := tc.existing.DeepCopy()
		client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(clone).Build()
		rv := NewRevisioner(client)
		created, err := rv.CreateConfigRevision(tc.revision)
		g.Expect(err).Should(BeNil(), "case: %s should create", tc.name)
		rehash := created.Name != tc.revision.Name
		g.Expect(rehash).Should(BeEquivalentTo(tc.rehash), "case: %s should rehash correctly", tc.name)
	}

	cases := []subcase{
		{
			name:     "create config revision",
			revision: t1Rev1,
			existing: &cloudv1.ConfigRevision{},
			rehash:   false,
		},
		// can no longer create a duplicated configRevision
		//{
		//	name:     "create config revision with existed",
		//	revision: t1Rev1,
		//	existing: t1Rev1,
		//	rehash:   false,
		//},
		// no longer support rehash automatically
		//{
		//	name:     "create config revision rehash",
		//	revision: t1Rev2,
		//	existing: t1Rev1,
		//	rehash:   true,
		//},
	}

	for _, c := range cases {
		testFn(c)
	}
}

//func TestRevisioner_RenewConfigRevision(t *testing.T) {
//	g := NewGomegaWithT(t)
//	scheme := runtime.NewScheme()
//	g.Expect(cloudv1.AddToScheme(scheme)).To(Succeed())
//	cc := &cloudv1.ClusterConfig{
//		ObjectMeta: v1.ObjectMeta{
//			Labels: map[string]string{
//				cloudv1.VersionLabel: "v6.1.0",
//				cloudv1.TenantLabel:  "1",
//			},
//		},
//		Spec: cloudv1.ClusterConfigSpec{
//			TiDB: &cloudv1.Configuration{
//				Config: cloudv1.NewTomlConfig(map[string]interface{}{
//					"token-limit": int64(1000),
//				}),
//			},
//			TiKV: &cloudv1.Configuration{
//				Config: cloudv1.NewTomlConfig(map[string]interface{}{
//					"abort-on-panic": false,
//				}),
//			},
//			PD: &cloudv1.Configuration{
//				Config: cloudv1.NewTomlConfig(map[string]interface{}{}),
//			},
//		},
//	}
//	rev := NewConfigRevision(cc)
//	before, err := time.Parse(time.RFC3339, rev.Annotations[cloudv1.ConfigRevisionActiveAnnotation])
//	g.Expect(err).Should(BeNil())
//	client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(rev.DeepCopy()).Build()
//	rv := NewRevisioner(client)
//	time.Sleep(1 * time.Second)
//	g.Expect(rv.RenewConfigRevision(rev)).Should(Succeed())
//	updated := &cloudv1.ConfigRevision{}
//	err = client.Get(context.TODO(), types.NamespacedName{Name: rev.Name}, updated)
//	g.Expect(err).Should(BeNil())
//	after, err := time.Parse(time.RFC3339, updated.Annotations[cloudv1.ConfigRevisionActiveAnnotation])
//	g.Expect(err).Should(BeNil())
//	g.Expect(before.Before(after)).To(BeTrue())
//}

func TestGenConfigRevisionName(t *testing.T) {
	type args struct {
		labels   map[string]string
		commitID string
		digest   string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "empty labels",
			args: args{
				labels:   nil,
				commitID: "abcdefghij",
				digest:   "digest",
			},
			want: "base-abcdefg-digest",
		}, {
			name: "full param",
			args: args{
				labels: map[string]string{
					cloudv1.VersionLabel:  "v7.1.1",
					cloudv1.TenantTagKey:  "123",
					cloudv1.ClusterTagKey: "456",
				},
				commitID: "abcdefghij",
				digest:   "digest",
			},
			want: "basev7.1.1-t123-c456-abcdefg-digest",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cc := &cloudv1.ClusterConfig{}
			cc.Labels = tt.args.labels
			cc.Spec.ConfigMeta = &cloudv1.ConfigMeta{
				GitCommitID: tt.args.commitID,
			}

			if got := GenConfigRevisionName(cc, tt.args.digest); got != tt.want {
				t.Errorf("GenConfigRevisionName() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestEqualRevision(t *testing.T) {
	nonEmpty := &cloudv1.ConfigRevision{
		Spec: cloudv1.ConfigRevisionSpec{
			TiDB: &cloudv1.Configuration{
				Config: cloudv1.NewTomlConfig(map[string]interface{}{
					"a": "b",
				}),
			},
		},
	}
	nonEmpty.Labels = map[string]string{
		cloudv1.ConfigRevisionHashLabel: "1d35b15d",
	}
	nonEmptyWithDiffConf := nonEmpty.DeepCopy()
	nonEmptyWithDiffConf.Spec.TiDB.Config.MP["x"] = "y"
	nonEmptyWithDiffHash := nonEmpty.DeepCopy()
	nonEmptyWithDiffHash.Labels[cloudv1.ConfigRevisionHashLabel] = "f4278b52"

	type args struct {
		lhs *cloudv1.ConfigRevision
		rhs *cloudv1.ConfigRevision
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{
			name: "both empty",
			args: args{lhs: &cloudv1.ConfigRevision{}, rhs: &cloudv1.ConfigRevision{}},
			want: true,
		},
		{
			name: "equal hash but diff conf",
			args: args{lhs: nonEmpty, rhs: nonEmptyWithDiffConf},
			want: false,
		},
		{
			name: "both hash and conf equal",
			args: args{lhs: nonEmpty, rhs: nonEmpty.DeepCopy()},
			want: true,
		},
		{
			name: "left empty",
			args: args{
				lhs: &cloudv1.ConfigRevision{},
				rhs: nonEmpty,
			},
			want: false,
		},
		// It seems like hash comparison logic is NOT work at all!
		// Should parse hash as hexadecimal num rather than decimalism.
		//{
		//	name: "hash non-equal",
		//	args: args{
		//		lhs: nonEmpty,
		//		rhs: nonEmptyWithDiffHash,
		//	},
		//	want: false,
		//},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := EqualRevision(tt.args.lhs, tt.args.rhs); got != tt.want {
				t.Errorf("EqualRevision() = %v, want %v", got, tt.want)
			}
		})
	}
}
