package cluster

import (
	"strconv"
	"testing"

	. "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"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

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

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

	// create test cluster
	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.UserEmailHeaderKey:        "foo@bar.com",
		metadata.IsCheckPermissionKey:      "true",
	}))
	req := &openapiserverlessv1beta1.CreateClusterRequest{
		Cluster: &openapiserverlessv1beta1.Cluster{
			DisplayName: "cluster-test-1",
			Region:      &openapicommonv1beta1.Region{Name: "regions/aws-us-east-1"},
			Labels: map[string]string{
				common.ProjectIDLabelKey: "1",
			},
		},
	}

	cluster, err := clsSvc.CreateCluster(ctx, req)
	g.Expect(err).ShouldNot(HaveOccurred())
	clusterId, err := strconv.ParseUint(cluster.ClusterId, 10, 64)
	g.Expect(err).ShouldNot(HaveOccurred())

	t.Run("List should success", func(t *testing.T) {
		req := &tidbmgmtservicev1.ListClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				TenantIds: []uint64{1},
			},
		}

		resp, err := service.ListClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Clusters).ShouldNot(BeNil())
		g.Expect(resp.Clusters).Should(HaveLen(1))
		g.Expect(resp.Clusters[0].ClusterId).Should(Equal(clusterId))
		g.Expect(resp.Clusters[0].ClusterName).Should(Equal(cluster.DisplayName))
		g.Expect(resp.Clusters[0].CreatedBy).Should(Equal("1"))
		g.Expect(resp.Clusters[0].CloudProvider).Should(Equal("aws"))
		g.Expect(resp.Clusters[0].DeployType.String()).Should(Equal("DEPLOY_TYPE_SERVERLESS"))
		g.Expect(resp.Clusters[0].GetServerlessCluster()).ShouldNot(BeNil())
		g.Expect(resp.Clusters[0].GetServerlessCluster().ClusterId).Should(Equal(cluster.ClusterId))
		g.Expect(resp.Clusters[0].CustomStatuses).Should(HaveLen(0))
		g.Expect(resp.Clusters[0].GetServerlessCluster().GetCluster().GetRegion().GetDisplayName()).
			Should(Equal(fakeRegionDisplayName(resp.Clusters[0].GetRegion())))
	})

	t.Run("List local should success", func(t *testing.T) {
		req := &tidbmgmtservicev1.ListClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				TenantIds: []uint64{1},
				View:      entity.ClusterViewClusterViewLocal,
			},
		}

		resp, err := service.ListClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Clusters).ShouldNot(BeNil())
		g.Expect(resp.Clusters).Should(HaveLen(1))
		g.Expect(resp.Clusters[0].ClusterId).Should(Equal(clusterId))
		g.Expect(resp.Clusters[0].ClusterName).Should(Equal(cluster.DisplayName))
		g.Expect(resp.Clusters[0].CreatedBy).Should(Equal("1"))
		g.Expect(resp.Clusters[0].CloudProvider).Should(Equal("aws"))
		g.Expect(resp.Clusters[0].DeployType.String()).Should(Equal("DEPLOY_TYPE_SERVERLESS"))
		g.Expect(resp.Clusters[0].GetServerlessCluster()).Should(BeNil())
		g.Expect(resp.Clusters[0].CustomStatuses).Should(HaveLen(0))
	})

	t.Run("List with cluster id should success", func(t *testing.T) {
		clusterId, err := strconv.ParseUint(cluster.ClusterId, 10, 64)
		g.Expect(err).ShouldNot(HaveOccurred())
		req := &tidbmgmtservicev1.ListClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				ClusterIds: []uint64{clusterId},
			},
		}

		resp, err := service.ListClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Clusters).ShouldNot(BeNil())
		g.Expect(resp.Clusters).Should(HaveLen(1))
		g.Expect(resp.Clusters[0].ClusterId).Should(Equal(clusterId))
		g.Expect(resp.Clusters[0].ClusterName).Should(Equal(cluster.DisplayName))
		g.Expect(resp.Clusters[0].CreatedBy).Should(Equal("1"))
		g.Expect(resp.Clusters[0].CloudProvider).Should(Equal("aws"))
		g.Expect(resp.Clusters[0].DeployType.String()).Should(Equal("DEPLOY_TYPE_SERVERLESS"))
		g.Expect(resp.Clusters[0].GetServerlessCluster()).ShouldNot(BeNil())
		g.Expect(resp.Clusters[0].GetServerlessCluster().ClusterId).Should(Equal(cluster.ClusterId))
		g.Expect(resp.Clusters[0].GetServerlessCluster().GetCluster().GetRegion().GetDisplayName()).
			Should(Equal(fakeRegionDisplayName(resp.Clusters[0].GetRegion())))
		g.Expect(resp.Clusters[0].CustomStatuses).Should(HaveLen(0))
	})

	t.Run("List with fake cluster id should success", func(t *testing.T) {
		req := &tidbmgmtservicev1.ListClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				ClusterIds: []uint64{1},
			},
		}

		resp, err := service.ListClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Clusters).Should(HaveLen(0))
	})

	t.Run("List with region should success", func(t *testing.T) {
		req := &tidbmgmtservicev1.ListClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				Regions:    []string{"us-east-1"},
				ProjectIds: []uint64{1},
			},
		}

		resp, err := service.ListClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Clusters).ShouldNot(BeNil())
		g.Expect(resp.Clusters).Should(HaveLen(1))
		g.Expect(resp.Clusters[0].ClusterId).Should(Equal(clusterId))
		g.Expect(resp.Clusters[0].ClusterName).Should(Equal(cluster.DisplayName))
		g.Expect(resp.Clusters[0].CreatedBy).Should(Equal("1"))
		g.Expect(resp.Clusters[0].CloudProvider).Should(Equal("aws"))
		g.Expect(resp.Clusters[0].DeployType.String()).Should(Equal("DEPLOY_TYPE_SERVERLESS"))
		g.Expect(resp.Clusters[0].GetServerlessCluster()).ShouldNot(BeNil())
		g.Expect(resp.Clusters[0].GetServerlessCluster().ClusterId).Should(Equal(cluster.ClusterId))
		g.Expect(resp.Clusters[0].CustomStatuses).Should(HaveLen(0))
		g.Expect(resp.Clusters[0].GetServerlessCluster().GetCluster().GetRegion().GetDisplayName()).
			Should(Equal(fakeRegionDisplayName(resp.Clusters[0].GetRegion())))
	})

	t.Run("List with wrong region should failed", func(t *testing.T) {
		req := &tidbmgmtservicev1.ListClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				Regions: []string{"us-west-2"},
			},
		}

		resp, err := service.ListClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Clusters).Should(HaveLen(0))
	})
}

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

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

	// create test cluster
	ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
		metadata.UserIDHeaderKey:           "1",
		metadata.OrgIDHeaderKey:            "2",
		metadata.DefaultProjectIDHeaderKey: "4",
		metadata.ProjectListHeaderKey:      "4",
		metadata.UserEmailHeaderKey:        "foo@bar.com",
		metadata.IsCheckPermissionKey:      "true",
	}))
	req := &openapiserverlessv1beta1.CreateClusterRequest{
		Cluster: &openapiserverlessv1beta1.Cluster{
			DisplayName: "cluster-test-2",
			Region:      &openapicommonv1beta1.Region{Name: "regions/aws-us-east-1"},
			Labels: map[string]string{
				common.ProjectIDLabelKey: "4",
			},
		},
	}

	_, err = clsSvc.CreateCluster(ctx, req)
	g.Expect(err).ShouldNot(HaveOccurred())

	t.Run("List should success", func(t *testing.T) {
		req := &tidbmgmtservicev1.CountClustersRequest{
			Filter: &tidbmgmtservicev1.ClusterFilter{
				ProjectIds: []uint64{4},
			},
		}

		resp, err := service.CountClusters(ctx, req)
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.Count).Should(Equal(uint32(1)))
	})
}

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

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

	ctx := grpcmetadata.NewIncomingContext(t.Context(), grpcmetadata.New(map[string]string{
		metadata.UserIDHeaderKey:           "1",
		metadata.OrgIDHeaderKey:            "3",
		metadata.DefaultProjectIDHeaderKey: "5",
		metadata.ProjectListHeaderKey:      "5",
		metadata.UserEmailHeaderKey:        "foo@bar.com",
		metadata.IsCheckPermissionKey:      "true",
	}))

	createReq := &openapiserverlessv1beta1.CreateClusterRequest{
		Cluster: &openapiserverlessv1beta1.Cluster{
			DisplayName: "cluster-test-labels",
			Region:      &openapicommonv1beta1.Region{Name: "regions/aws-us-east-1"},
			Labels: map[string]string{
				common.ProjectIDLabelKey: "5",
			},
		},
	}

	created, err := clsSvc.CreateCluster(ctx, createReq)
	g.Expect(err).ShouldNot(HaveOccurred())
	clusterID, err := strconv.ParseUint(created.ClusterId, 10, 64)
	g.Expect(err).ShouldNot(HaveOccurred())

	_, err = service.AddClusterLabels(ctx, &tidbmgmtservicev1.AddClusterLabelsRequest{
		ClusterId: clusterID,
		Labels: map[string]string{
			"env":   "dev",
			"owner": "team-a",
		},
	})
	g.Expect(err).ShouldNot(HaveOccurred())

	listResp, err := service.ListClusters(ctx, &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{ClusterIds: []uint64{clusterID}},
	})
	g.Expect(err).ShouldNot(HaveOccurred())
	g.Expect(listResp.GetClusters()).Should(HaveLen(1))
	g.Expect(listResp.GetClusters()[0].GetLabels()).Should(HaveKeyWithValue("env", "dev"))
	g.Expect(listResp.GetClusters()[0].GetLabels()).Should(HaveKeyWithValue("owner", "team-a"))
	g.Expect(listResp.GetClusters()[0].GetLabels()).Should(HaveKeyWithValue(common.ProjectIDLabelKey, "5"))
}
