package tidbscaler

import (
	"context"
	"testing"

	"github.com/go-logr/logr"
	. "github.com/onsi/gomega"
	"github.com/tidbcloud/aws-shared-provider/pkg/scheme"
	"github.com/tidbcloud/aws-shared-provider/pkg/types"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils/clientfake"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/controller-runtime/pkg/log"
)

func TestTidbScaler_Reduce(t *testing.T) {
	tidbPoolNamespace := "tidb-pool"
	tests := []struct {
		name        string
		step        int
		tidbPods    []client.Object
		wantCount   int
		wantDeletes []client.Object
	}{
		{
			name: "reduce step < all pods, delete one",
			step: 1,
			tidbPods: []client.Object{
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:              types.TidbServerNamePrefix + "1234567-name1",
					Namespace:         tidbPoolNamespace,
					CreationTimestamp: metav1.Now(),
				}},
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:      types.TidbServerNamePrefix + "7654321-name2",
					Namespace: tidbPoolNamespace,
				}},
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:      types.TidbServerNamePrefix + "8655321-name3",
					Namespace: tidbPoolNamespace,
				}},
			},
			wantCount: 2,
			wantDeletes: []client.Object{
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:      types.TidbServerNamePrefix + "7654321-name2",
					Namespace: tidbPoolNamespace,
				}},
			},
		},
		{
			name: "reduce step > all pods, delete all",
			step: 10,
			tidbPods: []client.Object{
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:      types.TidbServerNamePrefix + "1234567-name1",
					Namespace: tidbPoolNamespace,
				}},
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:      types.TidbServerNamePrefix + "7654321-name2",
					Namespace: tidbPoolNamespace,
				}},
			},
			wantCount: 0,
		},
		{
			name: "reduce step > all pods, delete nothing for young pods",
			step: 10,
			tidbPods: []client.Object{
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:              types.TidbServerNamePrefix + "1234567-name1",
					Namespace:         tidbPoolNamespace,
					CreationTimestamp: metav1.Now(),
				}},
				&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
					Name:              types.TidbServerNamePrefix + "7654321-name2",
					Namespace:         tidbPoolNamespace,
					CreationTimestamp: metav1.Now(),
				}},
			},
			wantCount: 2,
		},
	}
	for _, tt := range tests {
		g := NewGomegaWithT(t)
		t.Run(tt.name, func(t *testing.T) {
			tidbPods := make([]string, 0, len(tt.tidbPods))
			for _, pod := range tt.tidbPods {
				tidbPods = append(tidbPods, pod.GetName())
			}
			fakeTidbManager := &clientfake.FakeTidbManager{GetOldTidbFromFreeCacheR: tidbPods}
			ts := &TidbScaler{
				Logger:            logr.New(log.NullLogSink{}),
				tidbPoolNamespace: tidbPoolNamespace,
				client:            fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(tt.tidbPods...).Build(),
				tManager:          fakeTidbManager,
			}
			ts.Reduce(tt.step)

			pods := &corev1.PodList{}
			g.Expect(ts.client.List(context.Background(), pods, client.InNamespace(ts.tidbPoolNamespace))).To(Succeed())
			g.Expect(len(pods.Items)).To(Equal(tt.wantCount))
			for _, wantDelete := range tt.wantDeletes {
				err := ts.client.Get(context.Background(), client.ObjectKeyFromObject(wantDelete), &corev1.Pod{})
				g.Expect(err).To(MatchError(apierrors.IsNotFound, "IsNotFound"),
					"wantDelete: %s", wantDelete.GetName(),
				)
			}
		})
	}
}
