package cluster

import (
	"context"
	"errors"
	"strconv"
	"testing"
	"time"

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

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/app/register"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/repository"
	clustersvc "github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/service/cluster"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func TestCreateCluster(t *testing.T) {
	g := gomega.NewWithT(t)
	clusterId := allocateClusterId()

	type testCase struct {
		name      string
		inputsFn  func() (context.Context, *globalserverlessv1.ServerlessServiceCreateRequest)
		outputsFn func(*globalserverlessv1.ServerlessServiceCluster, error)
	}

	cases := []testCase{
		{
			name: "Create should success",
			inputsFn: func() (context.Context, *globalserverlessv1.ServerlessServiceCreateRequest) {
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            clusterId,
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
					SpendLimit: &globalserverlessv1.SpendLimit{
						Monthly: 1,
					},
				}

				return ctx, req
			},
			outputsFn: func(out *globalserverlessv1.ServerlessServiceCluster, err error) {
				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				var record *globalserverlessv1.ServerlessServiceCluster
				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: &globalserverlessv1.ClusterFilter{
							ClusterIds:  []string{out.Cluster.ClusterId},
							ShowDetails: true,
						},
					})

					g.Expect(err).ShouldNot(gomega.HaveOccurred())
					g.Expect(len(output)).Should(gomega.Equal(1))
					g.Expect(len(output[0].Features) > 0).Should(gomega.BeTrue())
					record = output[0]
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				g.Expect(out.SpendLimit == nil).To(gomega.BeTrue())

				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				g.Expect(record).ShouldNot(gomega.BeNil())
				g.Expect(record.TenantId).Should(gomega.Equal(uint64(1)))
				g.Expect(record.HasSetPwd).Should(gomega.Equal(false))
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(record.CloudProvider).Should(gomega.Equal(provider.Id.String()))
				g.Expect(record.Region).Should(gomega.Equal(regionEn.Id.String()))
			},
		},
		{
			name: "Create duplicated cluster id idempotently should success",
			inputsFn: func() (context.Context, *globalserverlessv1.ServerlessServiceCreateRequest) {
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            clusterId,
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
				}

				return ctx, req
			},
			outputsFn: func(cluster *globalserverlessv1.ServerlessServiceCluster, err error) {
				g.Expect(err).Should(gomega.BeNil())
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			ctx, req := tc.inputsFn()
			err := register.DiCall(func(svc clustersvc.Service) {
				resp, err := svc.Create(ctx, req)
				tc.outputsFn(resp, err)
			})

			g.Expect(err).ShouldNot(gomega.HaveOccurred())
		})
	}
}

func TestChangePassword(t *testing.T) {
	g := gomega.NewWithT(t)
	clusterId := allocateClusterId()

	type testCase struct {
		name      string
		inputsFn  func() (context.Context, *globalserverlessv1.ServerlessServiceListRequest)
		outputsFn func([]*globalserverlessv1.ServerlessServiceCluster, error)
	}

	cases := []testCase{
		{
			name: "List should success",
			inputsFn: func() (context.Context, *globalserverlessv1.ServerlessServiceListRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            clusterId,
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
					SpendLimit: &globalserverlessv1.SpendLimit{
						Monthly: 1,
					},
				}

				err := register.DiCall(func(svc clustersvc.Service) {
					_, err := svc.Create(ctx, req)
					g.Expect(err).ShouldNot(gomega.HaveOccurred())
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				err = register.DiCall(func(repo repository.ClusterRepo) {
					en, err := repo.Get(ctx, clusterId, false)
					g.Expect(err).ShouldNot(gomega.HaveOccurred())

					en.HasSetPwd = true
					err = repo.Save(ctx, en)
					g.Expect(err).ShouldNot(gomega.HaveOccurred())
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				return ctx, &globalserverlessv1.ServerlessServiceListRequest{
					Filter: &globalserverlessv1.ClusterFilter{
						ClusterIds: []string{clusterId},
					},
				}
			},
			outputsFn: func(items []*globalserverlessv1.ServerlessServiceCluster, err error) {
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(len(items) == 1).Should(gomega.BeTrue())

				cluster := items[0]
				g.Expect(cluster.HasSetPwd).Should(gomega.BeTrue())
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			ctx, req := tc.inputsFn()
			err := register.DiCall(func(svc clustersvc.Service) {
				resp, err := svc.List(ctx, req)

				tc.outputsFn(resp, err)
			})

			g.Expect(err).ShouldNot(gomega.HaveOccurred())
		})
	}
}

func TestUpdateCluster(t *testing.T) {
	g := gomega.NewWithT(t)
	clusterId := allocateClusterId()

	type testCase struct {
		name      string
		inputsFn  func() (context.Context, *globalserverlessv1.ServerlessServiceUpdateRequest)
		outputsFn func(*globalserverlessv1.ServerlessServiceUpdateResponse, error)
	}

	cases := []testCase{
		{
			name: "Update should success",
			inputsFn: func() (context.Context, *globalserverlessv1.ServerlessServiceUpdateRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            clusterId,
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
					SpendLimit: &globalserverlessv1.SpendLimit{
						Monthly: 1,
					},
				}

				err := register.DiCall(func(svc clustersvc.Service) {
					_, err := svc.Create(ctx, req)
					g.Expect(err).ShouldNot(gomega.HaveOccurred())
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				// wait initialize task done
				var cluster *globalserverlessv1.ServerlessServiceCluster
				for range 5 {
					err = register.DiCall(func(svc clustersvc.Service) error {
						output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
							Filter: &globalserverlessv1.ClusterFilter{
								ClusterIds:  []string{clusterId},
								ShowDetails: true,
							},
						})
						if err != nil {
							return err
						}

						if len(output) != 1 {
							return errors.New("cluster not found")
						}

						cluster = output[0]
						if cluster.UserStatus != globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL {
							return errors.New("cluster not ready")
						}

						return nil
					})
					if err == nil {
						break
					}
					time.Sleep(3 * time.Second) // wait for workflow done
				}

				g.Expect(cluster.UserStatus).
					Should(gomega.Equal(globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL))

				return ctx, &globalserverlessv1.ServerlessServiceUpdateRequest{
					Cluster: &globalserverlessv1.ServerlessServiceCluster{
						ClusterId: clusterId,
						Cluster: &regionalserverlessv1.ServerlessRegionalServiceClusterInfo{
							ClusterId: clusterId,
							Endpoints: &regionalserverlessv1.Endpoints{
								Public: &regionalserverlessv1.PublicEndpoint{
									Disabled: false,
								},
							},
						},
						TenantId:      1,
						CloudProvider: provider.Id.String(),
						Region:        regionEn.Id.String(),
						HasSetPwd:     false,
						BackupPolicy: &globalserverlessv1.AutomatedBackupPolicy{
							BackupTime: "6:30",
						},
						SpendLimit: &globalserverlessv1.SpendLimit{
							Monthly: 1,
						},
					},
				}
			},
			outputsFn: func(resp *globalserverlessv1.ServerlessServiceUpdateResponse, err error) {
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(resp.Cluster != nil).Should(gomega.BeTrue())
				g.Expect(resp.Cluster.Cluster != nil).Should(gomega.BeTrue())
				g.Expect(resp.Cluster.BackupPolicy != nil).Should(gomega.BeTrue())
				g.Expect(resp.Cluster.SpendLimit != nil).Should(gomega.BeTrue())
				g.Expect(resp.Cluster.TenantId == 1).Should(gomega.BeTrue())

				// Check the cluster in database
				var record *globalserverlessv1.ServerlessServiceCluster
				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: &globalserverlessv1.ClusterFilter{
							ClusterIds:  []string{resp.Cluster.ClusterId},
							ShowDetails: true,
						},
					})

					g.Expect(err).ShouldNot(gomega.HaveOccurred())
					g.Expect(len(output)).Should(gomega.Equal(1))
					record = output[0]
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				g.Expect(record).ShouldNot(gomega.BeNil())
				g.Expect(record.TenantId).Should(gomega.Equal(resp.Cluster.TenantId))
				g.Expect(record.HasSetPwd).Should(gomega.Equal(false))
				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				g.Expect(record.CloudProvider).Should(gomega.Equal(resp.Cluster.CloudProvider))
				g.Expect(record.Region).Should(gomega.Equal(resp.Cluster.Region))
				g.Expect(record.SpendLimit).ShouldNot(gomega.BeNil())
				g.Expect(record.UserStatus).
					Should(gomega.Equal(globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL))
				g.Expect(record.BackupPolicy).ShouldNot(gomega.BeNil())
			},
		},
		{
			name: "Update empty cluster id",
			inputsFn: func() (context.Context, *globalserverlessv1.ServerlessServiceUpdateRequest) {
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceUpdateRequest{
					Cluster: &globalserverlessv1.ServerlessServiceCluster{
						ClusterId: "update_cluster",
						Cluster: &regionalserverlessv1.ServerlessRegionalServiceClusterInfo{
							ClusterId: "update_cluster",
						},
						TenantId:      1,
						CloudProvider: provider.Id.String(),
						Region:        regionEn.Id.String(),
						HasSetPwd:     false,
						BackupPolicy: &globalserverlessv1.AutomatedBackupPolicy{
							BackupTime: "6:30",
						},
						SpendLimit: &globalserverlessv1.SpendLimit{
							Monthly: 1,
						},
					},
				}

				return ctx, req
			},
			outputsFn: func(resp *globalserverlessv1.ServerlessServiceUpdateResponse, err error) {
				g.Expect(err).Should(gomega.HaveOccurred())
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			ctx, req := tc.inputsFn()
			err := register.DiCall(func(svc clustersvc.Service) {
				resp, err := svc.Update(ctx, req)

				tc.outputsFn(resp, err)
			})

			g.Expect(err).ShouldNot(gomega.HaveOccurred())
		})
	}
}

func TestCountCluster(t *testing.T) {
	clusterId := allocateClusterId()

	type testCase struct {
		name      string
		inputsFn  func(t *testing.T, g *gomega.WithT) (context.Context, *globalserverlessv1.ClusterFilter)
		outputsFn func(t *testing.T, g *gomega.WithT, filter *globalserverlessv1.ClusterFilter, err error)
	}

	cases := []testCase{
		{
			name: "count normal cluster",
			inputsFn: func(t *testing.T, g *gomega.WithT) (context.Context, *globalserverlessv1.ClusterFilter) {
				t.Helper()
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            clusterId,
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
					SpendLimit: &globalserverlessv1.SpendLimit{
						Monthly: 1,
					},
				}

				err := register.DiCall(func(svc clustersvc.Service) {
					_, err := svc.Create(ctx, req)
					g.Expect(err).ShouldNot(gomega.HaveOccurred())
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				// wait initialize task done
				var cluster *globalserverlessv1.ServerlessServiceCluster
				for range 5 {
					err = register.DiCall(func(svc clustersvc.Service) error {
						output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
							Filter: &globalserverlessv1.ClusterFilter{
								ClusterIds:  []string{clusterId},
								ShowDetails: true,
							},
						})
						if err != nil {
							return err
						}

						if len(output) != 1 {
							return errors.New("cluster not found")
						}

						cluster = output[0]
						if cluster.UserStatus != globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL {
							return errors.New("cluster not ready")
						}

						return nil
					})
					if err == nil {
						break
					}
					time.Sleep(500 * time.Millisecond)
				}

				g.Expect(cluster.UserStatus).
					Should(gomega.Equal(globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL))

				return ctx, &globalserverlessv1.ClusterFilter{
					ClusterIds:  []string{clusterId},
					ShowDetails: true,
				}
			},
			outputsFn: func(t *testing.T, g *gomega.WithT, filter *globalserverlessv1.ClusterFilter, err error) {
				t.Helper()
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				// Check the cluster in database
				var record *globalserverlessv1.ServerlessServiceCluster
				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: filter,
					})
					g.Expect(err).ShouldNot(gomega.HaveOccurred())

					count, err := svc.Count(t.Context(), &globalserverlessv1.ServerlessServiceCountClustersRequest{
						Filter: filter,
					})
					g.Expect(err).ShouldNot(gomega.HaveOccurred())
					g.Expect(len(output)).Should(gomega.Equal(1))
					g.Expect(len(output)).Should(gomega.Equal(int(count)))
					record = output[0]
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				g.Expect(record).ShouldNot(gomega.BeNil())
				g.Expect(record.HasSetPwd).Should(gomega.Equal(false))
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(record.SpendLimit).ShouldNot(gomega.BeNil())
				g.Expect(record.UserStatus).
					Should(gomega.Equal(globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL))
				g.Expect(record.BackupPolicy).ShouldNot(gomega.BeNil())
			},
		},
		{
			name: "deleted cluster count",
			inputsFn: func(t *testing.T, g *gomega.WithT) (context.Context, *globalserverlessv1.ClusterFilter) {
				t.Helper()
				// Create a cluster first
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				return ctx, &globalserverlessv1.ClusterFilter{
					Deleted: proto.Bool(true),
				}
			},
			outputsFn: func(t *testing.T, g *gomega.WithT, filter *globalserverlessv1.ClusterFilter, err error) {
				t.Helper()
				g.Expect(err).Should(gomega.Succeed())
				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: filter,
					})
					g.Expect(err).ShouldNot(gomega.HaveOccurred())

					filter.ClusterIds = nil
					count, err := svc.Count(t.Context(), &globalserverlessv1.ServerlessServiceCountClustersRequest{
						Filter: filter,
					})
					g.Expect(err).ShouldNot(gomega.HaveOccurred())

					g.Expect(len(output)).Should(gomega.Equal(int(count)))
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			g := gomega.NewWithT(t)
			_, filter := tc.inputsFn(t, g)
			tc.outputsFn(t, g, filter, nil)
		})
	}
}

func TestDeleteCluster(t *testing.T) {
	g := gomega.NewWithT(t)
	clusterId := allocateClusterId()

	type testCase struct {
		name      string
		inputsFn  func() (context.Context, string)
		outputsFn func(*globalserverlessv1.ServerlessServiceCluster, error)
	}

	cases := []testCase{
		{
			name: "Delete should success",
			inputsFn: func() (context.Context, string) {
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            clusterId,
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
					SpendLimit: &globalserverlessv1.SpendLimit{
						Monthly: 1,
					},
				}

				err := register.DiCall(func(svc clustersvc.Service) {
					_, err := svc.Create(ctx, req)
					g.Expect(err).ShouldNot(gomega.HaveOccurred())
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				return ctx, clusterId
			},
			outputsFn: func(cluster *globalserverlessv1.ServerlessServiceCluster, err error) {
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: &globalserverlessv1.ClusterFilter{
							ClusterIds:  []string{cluster.ClusterId},
							ShowDetails: true,
							Deleted:     proto.Bool(true),
						},
					})

					g.Expect(err).ShouldNot(gomega.HaveOccurred())
					g.Expect(len(output)).Should(gomega.Equal(1))
					g.Expect(output[0].UserStatus).
						Should(gomega.Equal(globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_DELETED))
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: &globalserverlessv1.ClusterFilter{
							ClusterIds:  []string{cluster.ClusterId},
							ShowDetails: true,
							Deleted:     proto.Bool(false),
						},
					})

					g.Expect(err).ShouldNot(gomega.HaveOccurred())
					g.Expect(len(output)).Should(gomega.Equal(0))
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				// soft deleted
				var record *globalserverlessv1.ServerlessServiceCluster
				err = register.DiCall(func(svc clustersvc.Service) {
					output, err := svc.List(t.Context(), &globalserverlessv1.ServerlessServiceListRequest{
						Filter: &globalserverlessv1.ClusterFilter{
							ClusterIds:  []string{cluster.ClusterId},
							ShowDetails: true,
						},
					})

					g.Expect(err).ShouldNot(gomega.HaveOccurred())
					g.Expect(len(output)).Should(gomega.Equal(1))
					record = output[0]
				})
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(err).ShouldNot(gomega.HaveOccurred())
				g.Expect(cluster).ShouldNot(gomega.BeNil())
				g.Expect(cluster.TenantId).Should(gomega.Equal(record.TenantId))
				g.Expect(err).ShouldNot(gomega.HaveOccurred())

				g.Expect(cluster.CloudProvider).Should(gomega.Equal(record.CloudProvider))
				g.Expect(cluster.Region).Should(gomega.Equal(record.Region))
				g.Expect(cluster.SpendLimit).Should(gomega.BeNil())
				g.Expect(cluster.BackupPolicy).Should(gomega.BeNil())
				g.Expect(record.Cluster.DeleteTs > 0).Should(gomega.BeTrue())
				g.Expect(cluster.UserStatus).
					Should(gomega.Equal(globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_DELETED))
			},
		},
		{
			name: "Delete empty cluster id",
			inputsFn: func() (context.Context, string) {
				ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
					metadata.OrgIDHeaderKey:            "1",
					metadata.DefaultProjectIDHeaderKey: "1",
					metadata.ProjectListHeaderKey:      "1,2",
					metadata.UserIDHeaderKey:           "11",
				}))
				req := &globalserverlessv1.ServerlessServiceCreateRequest{
					Id:            "delete cluster id",
					TenantId:      1,
					ProjectId:     1,
					CloudProvider: provider.Id.String(),
					Region:        regionEn.Id.String(),
					SpendLimit: &globalserverlessv1.SpendLimit{
						Monthly: 1,
					},
				}

				return ctx, req.Id
			},
			outputsFn: func(cluster *globalserverlessv1.ServerlessServiceCluster, err error) {
				g.Expect(err).Should(gomega.HaveOccurred())
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			ctx, req := tc.inputsFn()
			err := register.DiCall(func(svc clustersvc.Service) {
				cluster, err := svc.Delete(ctx, req, false)
				tc.outputsFn(cluster, err)
			})
			g.Expect(err).ShouldNot(gomega.HaveOccurred())
		})
	}
}

func TestClusterRepo(t *testing.T) {
	g := gomega.NewWithT(t)
	clusterId := allocateClusterId()
	ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
		metadata.OrgIDHeaderKey:            "1",
		metadata.DefaultProjectIDHeaderKey: "1",
		metadata.ProjectListHeaderKey:      "1,2",
		metadata.UserIDHeaderKey:           "11",
	}))
	req := &globalserverlessv1.ServerlessServiceCreateRequest{
		Id:            clusterId,
		TenantId:      1,
		ProjectId:     1,
		CloudProvider: provider.Id.String(),
		Region:        regionEn.Id.String(),
		SpendLimit: &globalserverlessv1.SpendLimit{
			Monthly: 1,
		},
	}

	err := register.DiCall(func(svc clustersvc.Service) {
		_, err := svc.Create(ctx, req)
		g.Expect(err).ShouldNot(gomega.HaveOccurred())
	})
	g.Expect(err).Should(gomega.Succeed())

	err = register.DiCall(func(repo repository.ClusterRepo) {
		clusters, err := repo.Query(ctx, &repository.ClusterQueryParameter{
			TenantIds: []uint64{11112},
		})
		g.Expect(err).Should(gomega.Succeed())
		g.Expect(len(clusters) == 0).Should(gomega.BeTrue())

		clusters, err = repo.Query(ctx, &repository.ClusterQueryParameter{
			ProjectIds: []uint64{11112},
		})
		g.Expect(err).Should(gomega.Succeed())
		g.Expect(len(clusters) == 0).Should(gomega.BeTrue())

		clusters, err = repo.Query(ctx, &repository.ClusterQueryParameter{
			CloudProviderNames: []globalcommonv1.CloudProvider_Name{globalcommonv1.CloudProvider_AZURE},
		})
		g.Expect(err).Should(gomega.Succeed())
		g.Expect(len(clusters) == 0).Should(gomega.BeTrue())

		clusters, err = repo.Query(ctx, &repository.ClusterQueryParameter{
			TenantIds: []uint64{req.TenantId},
		})
		g.Expect(err).Should(gomega.Succeed())
		g.Expect(len(clusters) > 0).Should(gomega.BeTrue())

		clusters, err = repo.Query(ctx, &repository.ClusterQueryParameter{
			ProjectIds: []uint64{req.ProjectId},
		})
		g.Expect(err).Should(gomega.Succeed())
		g.Expect(len(clusters) > 0).Should(gomega.BeTrue())

		clusters, err = repo.Query(ctx, &repository.ClusterQueryParameter{
			CloudProviderNames: []globalcommonv1.CloudProvider_Name{globalcommonv1.CloudProvider_AWS},
		})
		g.Expect(err).Should(gomega.Succeed())
		g.Expect(len(clusters) > 0).Should(gomega.BeTrue())
	})
	g.Expect(err).Should(gomega.Succeed())
}

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

	ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
		metadata.OrgIDHeaderKey:            "1",
		metadata.DefaultProjectIDHeaderKey: "1",
		metadata.ProjectListHeaderKey:      "1,2",
		metadata.UserIDHeaderKey:           "11",
	}))
	clusterId1 := allocateClusterId()
	clusterId2 := allocateClusterId()

	projectId, err := strconv.ParseUint(allocateClusterId(), 10, 64)
	g.Expect(err).To(gomega.Succeed())

	requests := []*globalserverlessv1.ServerlessServiceCreateRequest{
		{
			Id:            clusterId1,
			TenantId:      1,
			ProjectId:     projectId,
			CloudProvider: provider.Id.String(),
			Region:        regionEn.Id.String(),
			SpendLimit: &globalserverlessv1.SpendLimit{
				Monthly: 1,
			},
		},
		{
			Id:            clusterId2,
			TenantId:      1,
			ProjectId:     projectId,
			CloudProvider: provider.Id.String(),
			Region:        regionEn.Id.String(),
			SpendLimit: &globalserverlessv1.SpendLimit{
				Monthly: 1,
			},
		},
	}

	for _, req := range requests {
		err := register.DiCall(func(svc clustersvc.Service) {
			_, err := svc.Create(ctx, req)
			g.Expect(err).To(gomega.Succeed())
		})
		g.Expect(err).To(gomega.Succeed())
	}

	filter := &globalserverlessv1.ServerlessServiceListRequest{
		Filter: &globalserverlessv1.ClusterFilter{
			ProjectIds: []uint64{projectId},
		},
	}

	expectOut := map[string]string{
		clusterId1: clusterId1,
		clusterId2: clusterId2,
	}

	err = register.DiCall(func(svc clustersvc.Service) {
		clusters, err := svc.List(ctx, filter)
		g.Expect(err).To(gomega.Succeed())

		g.Expect(len(clusters)).To(gomega.Equal(len(requests)))

		g.Expect(expectOut[clusters[0].ClusterId]).To(gomega.Equal(clusters[0].ClusterId))
		g.Expect(expectOut[clusters[1].ClusterId]).To(gomega.Equal(clusters[1].ClusterId))
	})
	g.Expect(err).To(gomega.Succeed())
}
