package cluster

import (
	"context"
	"fmt"
	"testing"
	"time"

	. "github.com/onsi/gomega"
	grpcmetadata "google.golang.org/grpc/metadata"

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapiserverlessv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/serverless/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/apimiddleware/filter"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func TestTranspileFilterReq(t *testing.T) {
	t.Parallel()

	g := NewWithT(t)
	clusterFilter := &repository.ClusterFilter{}
	filterStr := `region.provider = "aws" AND state = "creating" AND clusterId = "10000000000000000001,10000000000000000002,1379661944646344403" AND projectId = 1372813089202551283 AND labels."tidb.cloud/project" = "123,2222"`

	params, err := filter.TranspileFilter(&openapiserverlessv1beta1.ListClustersRequest{Filter: filterStr})
	g.Expect(err).ShouldNot(HaveOccurred())
	g.Expect(params["labels."+common.ProjectIDLabelKey]).Should(BeNil())

	err = cvtGlobalFilter(params, clusterFilter)
	g.Expect(err).ShouldNot(HaveOccurred())
	g.Expect(clusterFilter.CloudProviders).Should(HaveExactElements([]string{openapicommonv1beta1.Region_aws.String()}))
	g.Expect(clusterFilter.States).
		Should(HaveExactElements([]openapicommonv1beta1.ClusterState{openapicommonv1beta1.ClusterState_CREATING}))
	g.Expect(clusterFilter.ClusterIds).
		Should(HaveExactElements([]uint64{10000000000000000001, 10000000000000000002, 1379661944646344403}))
	g.Expect(clusterFilter.ProjectIds).Should(HaveExactElements([]uint64{1372813089202551283, 123, 2222}))
}

func TestListServerlessCluster(t *testing.T) {
	g := NewWithT(t)

	service, err := initService()
	g.Expect(err).ShouldNot(HaveOccurred())

	type testCase struct {
		name      string
		inputsFn  func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest)
		outputsFn func(*openapiserverlessv1beta1.ListClustersResponse, error)
	}

	cases := []testCase{
		{
			name: "List should success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-1",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
						},
					},
				}
				c, err := service.CreateCluster(ctx, req)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := fmt.Sprintf(`clusterId = %q`, c.ClusterId)
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(1)))
				g.Expect(clusters.Clusters[0].Name).Should(Equal(
					fmt.Sprintf("clusters/%s", clusters.Clusters[0].ClusterId)))
				g.Expect(clusters.Clusters[0].DisplayName).Should(Equal("list-test-1"))
				g.Expect(clusters.Clusters[0].Version).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].UserPrefix).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(clusters.Clusters[0].GetRegion().GetRegionId())))
			},
		},
		{
			name: "List filter multi label success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-2",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
							"env":                    "dev",
						},
					},
				}
				c, err := service.CreateCluster(ctx, req)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := fmt.Sprintf(`clusterId = %q AND labels.env = "dev"`, c.ClusterId)
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(1)))
				g.Expect(clusters.Clusters[0].Name).Should(Equal(
					fmt.Sprintf("clusters/%s", clusters.Clusters[0].ClusterId)))
				g.Expect(clusters.Clusters[0].DisplayName).Should(Equal("list-test-2"))
				g.Expect(clusters.Clusters[0].Version).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].UserPrefix).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(clusters.Clusters[0].GetRegion().GetRegionId())))
			},
		},
		{
			name: "List filter multi label success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-3",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
							"env":                    "dev",
							"project":                "test",
						},
					},
				}
				c, err := service.CreateCluster(ctx, req)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := fmt.Sprintf(`clusterId = %q AND labels.env = "dev" AND labels.project = "test"`, c.ClusterId)
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(1)))
				g.Expect(clusters.Clusters[0].Name).Should(Equal(
					fmt.Sprintf("clusters/%s", clusters.Clusters[0].ClusterId)))
				g.Expect(clusters.Clusters[0].DisplayName).Should(Equal("list-test-3"))
				g.Expect(clusters.Clusters[0].Version).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].UserPrefix).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(clusters.Clusters[0].GetRegion().GetRegionId())))
			},
		},
		{
			name: "List filter multi label failed",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-4",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
							"env":                    "dev",
							"project":                "test",
						},
					},
				}
				c, err := service.CreateCluster(ctx, req)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := fmt.Sprintf(`clusterId = %q AND labels.env = "dev" AND labels.project = "mgmt"`, c.ClusterId)
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(0)))
			},
		},
		{
			name: "List filter by page",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req1 := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-5",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
							"env":                    "dev",
							"project":                "page",
						},
					},
				}
				_, err := service.CreateCluster(ctx, req1)
				g.Expect(err).ShouldNot(HaveOccurred())
				time.Sleep(3 * time.Second)
				req2 := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-6",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
							"env":                    "dev",
							"project":                "page",
						},
					},
				}
				_, err = service.CreateCluster(ctx, req2)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := `labels.project = "page"`
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f, PageSize: 1}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(2)))
				g.Expect(len(clusters.Clusters)).Should(Equal(1))
				g.Expect(clusters.Clusters[0].Name).Should(Equal(
					fmt.Sprintf("clusters/%s", clusters.Clusters[0].ClusterId)))
				g.Expect(clusters.Clusters[0].DisplayName).Should(Equal("list-test-6"))
				g.Expect(clusters.Clusters[0].Version).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].UserPrefix).ShouldNot(BeEmpty())
				g.Expect(clusters.NextPageToken).ShouldNot(Equal(""))
				g.Expect(clusters.Clusters[0].GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(clusters.Clusters[0].GetRegion().GetRegionId())))
			},
		},
		{
			name: "List filter by displayName should success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-displayname-filter",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-east-1",
						},
					},
				}
				_, err := service.CreateCluster(ctx, req)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := `displayName = "list-displayname-filter"`
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(1)))
				g.Expect(clusters.Clusters[0].Name).Should(Equal(
					fmt.Sprintf("clusters/%s", clusters.Clusters[0].ClusterId)))
				g.Expect(clusters.Clusters[0].DisplayName).Should(Equal("list-displayname-filter"))
				g.Expect(clusters.Clusters[0].Version).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].UserPrefix).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(clusters.Clusters[0].GetRegion().GetRegionId())))
			},
		},
		{
			name: "List should success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.ListClustersRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.UserIDHeaderKey:           "1",
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2,3",
					metadata.IsCheckPermissionKey:      "true",
				}))

				req := &openapiserverlessv1beta1.CreateClusterRequest{
					Cluster: &openapiserverlessv1beta1.Cluster{
						DisplayName: "list-test-7",
						Region: &openapicommonv1beta1.Region{
							Name: "regions/aws-us-west-2",
						},
						Labels: map[string]string{
							common.ProjectIDLabelKey: "1",
						},
					},
				}
				_, err := service.CreateCluster(ctx, req)
				g.Expect(err).ShouldNot(HaveOccurred())

				// Build get request
				f := `region.name = "regions/aws-us-west-2"`
				return ctx, &openapiserverlessv1beta1.ListClustersRequest{Filter: f}
			},
			outputsFn: func(clusters *openapiserverlessv1beta1.ListClustersResponse, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(clusters).ShouldNot(BeNil())
				g.Expect(clusters.TotalSize).Should(Equal(uint32(1)))
				g.Expect(clusters.Clusters[0].Name).Should(Equal(
					fmt.Sprintf("clusters/%s", clusters.Clusters[0].ClusterId)))
				g.Expect(clusters.Clusters[0].DisplayName).Should(Equal("list-test-7"))
				g.Expect(clusters.Clusters[0].Version).ShouldNot(BeEmpty())
				g.Expect(clusters.Clusters[0].UserPrefix).ShouldNot(BeEmpty())
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			ctx, req := tc.inputsFn()
			clusters, err := service.ListClusters(ctx, req)
			tc.outputsFn(clusters, err)
		})
	}
}
