//go:build unit || all
// +build unit all

package common

import (
	"context"
	"fmt"
	"strings"
	"testing"

	. "github.com/onsi/gomega"
	opv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"helm.sh/helm/v3/pkg/action"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	"github.com/tidbcloud/eks-provider/controllers/cluster/task"
	"github.com/tidbcloud/eks-provider/utils/tidb-operator/tidbcluster"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/pkg/dataplane"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/pkg/scheme"
)

func TestTidbInitializer(t *testing.T) {
	cases := map[string]struct {
		context   *task.Context
		expectErr bool
		check     func(*opv1alpha1.TidbInitializer) error
	}{
		"tidb initializer contains tag": {
			context: &task.Context{
				Tidbcluster: &opv1alpha1.TidbCluster{
					ObjectMeta: metav1.ObjectMeta{
						Name: "test-tc",
					},
					Spec: opv1alpha1.TidbClusterSpec{
						TiDB: &opv1alpha1.TiDBSpec{},
					},
				},
				LegacyNetwork: &cloudv1.Network{},
				Cluster: &cloudv1.Cluster{
					ObjectMeta: metav1.ObjectMeta{
						Name: "test-cluster",
					},
				},
				Namer: namer.NewNamer(),
			},
			check: func(ti *opv1alpha1.TidbInitializer) error {
				if !strings.Contains(ti.Spec.Image, ":") {
					return fmt.Errorf("image '%s' doesn't contain tag", ti.Spec.Image)
				}
				return nil
			},
		},
		"tidb initializer with feature gate": {
			context: &task.Context{
				Tidbcluster: &opv1alpha1.TidbCluster{
					ObjectMeta: metav1.ObjectMeta{
						Name: "test-tc",
					},
					Spec: opv1alpha1.TidbClusterSpec{
						Version: "v5.4.1",
						TiDB:    &opv1alpha1.TiDBSpec{},
					},
				},
				LegacyNetwork: &cloudv1.Network{},
				Cluster: &cloudv1.Cluster{
					ObjectMeta: metav1.ObjectMeta{
						Name: "test-cluster",
					},
				},
				Config: config.RootConfig{
					ControllerConfig: config.ControllerConfig{
						ExpFeatureGates: []config.ExpFeatureGate{
							{
								Name:              "StatsSyncLoad",
								MinSupportVersion: "v5.4.0",
								Command:           "SET GLOBAL tidb_stats_load_sync_wait=100; SET GLOBAL tidb_stats_load_pseudo_timeout=ON;",
								FeatureGate: config.FeatureGate{
									All: true,
								},
							},
						},
					},
				},
				Namer: namer.NewNamer(),
			},
			check: func(ti *opv1alpha1.TidbInitializer) error {
				initSql := *ti.Spec.InitSql
				if !strings.Contains(initSql, "tidb_stats_load_sync_wait") {
					fmt.Println(initSql)
					return fmt.Errorf("doesn't contain desired command %s", "tidb_stats_load_sync_wait")
				}
				return nil
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(tc.context.Tidbcluster, tc.context.Cluster).Build()
			tc.context.TCGetter = tidbcluster.NewV1Getter(tc.context.Tidbcluster)
			tc.context.Local = cli
			tc.context.LegacyRemote = &fakeRemoteGetter{client: cli}
			tc.context.EnvProvider = &dataplane.FakeEnvProvider{
				Client: cli,
			}

			ti := NewTiDBInitializer(tc.context)
			res := ti.Create(context.TODO())
			if !tc.expectErr {
				g.Expect(res.Error()).To(Succeed())
			} else {
				g.Expect(res.Error()).NotTo(Succeed())
				return
			}
			initializer := &opv1alpha1.TidbInitializer{}
			g.Expect(cli.Get(context.TODO(), types.NamespacedName{Name: tc.context.Namer.GetTiDBClusterName()}, initializer)).To(Succeed())
			g.Expect(tc.check(initializer)).To(Succeed())
		})
	}
}

type fakeRemoteGetter struct {
	client client.Client
}

func (f *fakeRemoteGetter) GetClient(context.Context) (client.Client, error) {
	return f.client, nil
}

func (f *fakeRemoteGetter) GetHelmConfig(context.Context) (*action.Configuration, error) {
	panic("not implemented")
}

var _ k8s.ClientGetter = (*fakeRemoteGetter)(nil)
