package revision

import (
	"context"
	"testing"
	"time"

	. "github.com/onsi/gomega"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"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.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{}{}),
			},
			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,
		},
		{
			name:     "create config revision with existed",
			revision: t1Rev1,
			existing: t1Rev1,
			rehash:   false,
		},
		{
			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())
}
