package common

import (
	"context"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	appsv1 "k8s.io/api/apps/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestGoldpinger(t *testing.T) {
	cases := map[string]struct {
		config      config.InfraComponentConfig
		annotations map[string]string
		input       *appsv1.DaemonSet
		expect      func(g *WithT, createRes, postCreateRes deploy.Result)
	}{
		"skip when chartPath is empty": {
			config:      config.InfraComponentConfig{},
			annotations: map[string]string{v1alpha1.AnnotationKeyEnableGoldpinger: "true"},
			expect: func(g *WithT, createRes, postCreateRes deploy.Result) {
				g.Expect(createRes.Error()).Should(Succeed())
				g.Expect(postCreateRes.Error()).Should(Succeed())
			},
		},
		"skip when annotation not set": {
			config: config.InfraComponentConfig{
				HelmConfig:  config.HelmConfig{ChartPath: "/etc/helm/goldpinger/goldpinger.tgz"},
				ReleaseName: "goldpinger",
				Namespace:   "monitoring",
			},
			expect: func(g *WithT, createRes, postCreateRes deploy.Result) {
				g.Expect(createRes.Error()).Should(Succeed())
				g.Expect(postCreateRes.Error()).Should(Succeed())
			},
		},
		"skip when annotation is not true": {
			config: config.InfraComponentConfig{
				HelmConfig:  config.HelmConfig{ChartPath: "/etc/helm/goldpinger/goldpinger.tgz"},
				ReleaseName: "goldpinger",
				Namespace:   "monitoring",
			},
			annotations: map[string]string{v1alpha1.AnnotationKeyEnableGoldpinger: "false"},
			expect: func(g *WithT, createRes, postCreateRes deploy.Result) {
				g.Expect(createRes.Error()).Should(Succeed())
				g.Expect(postCreateRes.Error()).Should(Succeed())
			},
		},
		"daemonset ready": {
			config: config.InfraComponentConfig{
				HelmConfig:  config.HelmConfig{ChartPath: "/etc/helm/goldpinger/goldpinger.tgz"},
				ReleaseName: "goldpinger",
				Namespace:   "monitoring",
			},
			annotations: map[string]string{v1alpha1.AnnotationKeyEnableGoldpinger: "true"},
			input: &appsv1.DaemonSet{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "goldpinger",
					Namespace: "monitoring",
				},
				Status: appsv1.DaemonSetStatus{
					NumberReady:            3,
					DesiredNumberScheduled: 3,
				},
			},
			expect: func(g *WithT, createRes, postCreateRes deploy.Result) {
				// Create would fail because helm chart file doesn't exist,
				// so we only verify PostCreate with a ready daemonset.
				g.Expect(postCreateRes.Error()).Should(Succeed())
			},
		},
		"daemonset not ready": {
			config: config.InfraComponentConfig{
				HelmConfig:  config.HelmConfig{ChartPath: "/etc/helm/goldpinger/goldpinger.tgz"},
				ReleaseName: "goldpinger",
				Namespace:   "monitoring",
			},
			annotations: map[string]string{v1alpha1.AnnotationKeyEnableGoldpinger: "true"},
			input: &appsv1.DaemonSet{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "goldpinger",
					Namespace: "monitoring",
				},
				Status: appsv1.DaemonSetStatus{
					NumberReady:            1,
					DesiredNumberScheduled: 3,
				},
			},
			expect: func(g *WithT, createRes, postCreateRes deploy.Result) {
				g.Expect(postCreateRes.Error()).To(HaveOccurred())
			},
		},
	}

	for name, c := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewWithT(t)
			cli := fake.NewClientBuilder().Build()
			if c.input != nil {
				g.Expect(cli.Create(context.Background(), c.input)).To(Succeed())
			}

			appbox := &v1alpha1.APPBox{
				ObjectMeta: metav1.ObjectMeta{
					Name:        "test-appbox",
					Namespace:   "default",
					Annotations: c.annotations,
				},
			}

			goldpinger := NewGoldpinger(&task.Context{APPBox: appbox})
			goldpinger.Remote = &controllers.MockClientGetter{Client: cli}
			goldpinger.InfraTmplConfig = &config.InfraTemplateConfig{
				Goldpinger: c.config,
			}

			createRes := goldpinger.Create(context.Background())
			postCreateRes := goldpinger.PostCreate(context.Background())
			c.expect(g, createRes, postCreateRes)
		})
	}
}
