package common

import (
	"context"
	"fmt"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"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"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

const (
	clusterGatewayServiceName = "clustergateway-kong-proxy"
)

func TestClusterGatewayPostCreate(t *testing.T) {
	cases := map[string]struct {
		provier string
		service *corev1.Service
		expect  func(g *WithT, task ClusterGateway, res deploy.Result)
	}{
		"service notfound": {
			provier: "eks",
			service: &corev1.Service{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "notfound",
					Namespace: "notfound",
				},
			},
			expect: func(g *WithT, task ClusterGateway, res deploy.Result) {
				err := res.Error()
				g.Expect(err).To(HaveOccurred())
				g.Expect(err.Error()).To(ContainSubstring("not found"))
			},
		},
		"service created but lb not ready": {
			provier: "eks",
			service: &corev1.Service{
				ObjectMeta: metav1.ObjectMeta{
					Name:      clusterGatewayServiceName,
					Namespace: "tidb-admin",
				},
				Spec: corev1.ServiceSpec{
					Type: corev1.ServiceTypeLoadBalancer,
				},
			},
			expect: func(g *WithT, task ClusterGateway, res deploy.Result) {
				g.Expect(res).To(Equal(deploy.Error(fmt.Errorf("waiting for cluster gateway load balancer be provisioned"))))
			},
		},
		"eks service and lb ready": {
			provier: "eks",
			service: &corev1.Service{
				ObjectMeta: metav1.ObjectMeta{
					Name:      clusterGatewayServiceName,
					Namespace: "tidb-admin",
				},
				Spec: corev1.ServiceSpec{
					Type: corev1.ServiceTypeLoadBalancer,
				},
				Status: corev1.ServiceStatus{
					LoadBalancer: corev1.LoadBalancerStatus{
						Ingress: []corev1.LoadBalancerIngress{
							{
								IP:       "1.2.3.4",
								Hostname: "cplb.aws.com",
							},
						},
					},
				},
			},
			expect: func(g *WithT, task ClusterGateway, res deploy.Result) {
				g.Expect(res.Error()).Should(Succeed())
				g.Expect(task.APPBox.Status.GatewayBaseUrl).To(Equal("cplb.aws.com"))
			},
		},
		"gke service and lb ready": {
			provier: "gke",
			service: &corev1.Service{
				ObjectMeta: metav1.ObjectMeta{
					Name:      clusterGatewayServiceName,
					Namespace: "tidb-admin",
				},
				Spec: corev1.ServiceSpec{
					Type: corev1.ServiceTypeLoadBalancer,
				},
				Status: corev1.ServiceStatus{
					LoadBalancer: corev1.LoadBalancerStatus{
						Ingress: []corev1.LoadBalancerIngress{
							{
								IP:       "1.2.3.4",
								Hostname: "cplb.aws.com",
							},
						},
					},
				},
			},
			expect: func(g *WithT, task ClusterGateway, res deploy.Result) {
				g.Expect(res.Error()).Should(Succeed())
				g.Expect(task.APPBox.Status.GatewayBaseUrl).To(Equal("1.2.3.4"))
			},
		},
	}

	for n, c := range cases {
		t.Run(n, func(t *testing.T) {
			g := NewWithT(t)
			cli := fake.NewClientBuilder().Build()
			g.Expect(cli.Create(context.Background(), c.service)).To(Succeed())
			g.Expect(cli.Create(context.Background(), cgDeployment())).To(Succeed())

			task := NewClusterGateway(&task.Context{
				Remote: &controllers.MockClientGetter{Client: cli},
				APPBox: defaultAPPBox(withAppBoxProvider(c.provier)),
				DedicatedTmplConfig: &config.DedicatedTemplateConfig{
					ClusterGateway: config.InfraComponentConfig{
						ReleaseName: "clustergateway",
						Namespace:   "tidb-admin",
					},
				},
			})
			c.expect(g, *task, task.PostCreate(context.Background()))
		})
	}
}

func TestClusterGatewayDelete(t *testing.T) {
	cases := map[string]struct {
		service *corev1.Service
		expect  func(g *WithT, res deploy.Result)
	}{
		"empty client": {
			service: &corev1.Service{
				ObjectMeta: metav1.ObjectMeta{
					Name:      clusterGatewayServiceName,
					Namespace: "tidb-admin",
				},
			},
			expect: func(g *WithT, res deploy.Result) {
				g.Expect(res).To(Equal(deploy.Done()))
			},
		},
		"service present": {
			service: &corev1.Service{
				ObjectMeta: metav1.ObjectMeta{
					Name:      clusterGatewayServiceName,
					Namespace: "tidb-admin",
				},
			},
			expect: func(g *WithT, res deploy.Result) {
				g.Expect(res.Error()).ShouldNot(BeNil())
				g.Expect(res.Error().Error()).To(ContainSubstring(fmt.Sprintf("%v still exist", clusterGatewayServiceName)))
			},
		},
		"service deleted": {
			service: nil,
			expect: func(g *WithT, res deploy.Result) {
				g.Expect(res).To(Equal(deploy.Done()))
			},
		},
	}

	for n, c := range cases {
		t.Run(n, func(t *testing.T) {
			g := NewWithT(t)
			cli := fake.NewClientBuilder().Build()
			if c.service != nil {
				g.Expect(cli.Create(context.Background(), c.service)).To(Succeed())
			}
			task := NewClusterGateway(&task.Context{
				Remote: &controllers.MockClientGetter{Client: cli},
				APPBox: defaultAPPBox(),
				DedicatedTmplConfig: &config.DedicatedTemplateConfig{
					ClusterGateway: config.InfraComponentConfig{
						ReleaseName: "clustergateway-kong",
						Namespace:   "tidb-admin",
					},
				},
			})
			if n == "empty client" {
				task.Remote = &k8s.EmptyGetter{}
			}
			c.expect(g, task.Delete(context.Background()))
		})
	}
}

func TestClusterGatewayToHelmValues(t *testing.T) {
	cfg := ClusterGatewayConfig{
		InfraComponentConfig: config.InfraComponentConfig{
			ReleaseName: "clustergateway-kong",
			Namespace:   "tidb-admin",
			HelmValues: map[string]interface{}{
				"proxy": map[string]interface{}{
					"enabled": true,
					"type":    "LoadBalancer",
					"annotations": map[string]interface{}{
						"service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags": "tags",
					},
				},
			},
		},
		lbServiceTags: map[string]string{
			"cloud.tidbcloud.com/tenant": "1234",
		},
	}
	vals := cfg.ToHelmValues()
	g := NewWithT(t)
	fmt.Println(vals)
	g.Expect(vals["proxy"].(map[string]interface{})["annotations"].(map[string]interface{})["service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags"]).To(Equal("cloud.tidbcloud.com/tenant=1234"))
}

func cgDeployment() *appsv1.Deployment {
	return &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "clustergateway-kong",
			Namespace: "tidb-admin",
		},
		Status: appsv1.DeploymentStatus{
			Conditions: []appsv1.DeploymentCondition{
				{
					Type:   appsv1.DeploymentAvailable,
					Status: "True",
				},
			},
		},
	}
}
