package cluster

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

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

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

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

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

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

	cases := []testCase{
		{
			name: "Get should success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.GetClusterRequest) {
				// 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: "get-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
				return ctx, &openapiserverlessv1beta1.GetClusterRequest{ClusterId: c.ClusterId}
			},
			outputsFn: func(cluster *openapiserverlessv1beta1.Cluster, err error) {
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(cluster).ShouldNot(BeNil())
				g.Expect(cluster.ClusterId).ShouldNot(BeEmpty())
				g.Expect(cluster.Name).Should(Equal(fmt.Sprintf("clusters/%s", cluster.ClusterId)))
				g.Expect(cluster.DisplayName).Should(Equal("get-test-1"))
				g.Expect(cluster.Version).ShouldNot(BeEmpty())
				g.Expect(cluster.UserPrefix).ShouldNot(BeEmpty())
				g.Expect(cluster.GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(cluster.GetRegion().GetRegionId())))
			},
		},
		{
			name: "Get full-view should success",
			inputsFn: func() (context.Context, *openapiserverlessv1beta1.GetClusterRequest) {
				// 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: "get-test-2",
						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
				return ctx, &openapiserverlessv1beta1.GetClusterRequest{
					ClusterId: c.ClusterId,
					View:      openapiserverlessv1beta1.ClusterView_FULL,
				}
			},
			outputsFn: func(cluster *openapiserverlessv1beta1.Cluster, err error) {
				// Check the cluster in response
				g.Expect(err).ShouldNot(HaveOccurred())
				g.Expect(cluster).ShouldNot(BeNil())
				g.Expect(cluster.ClusterId).ShouldNot(BeEmpty())
				g.Expect(cluster.Name).Should(Equal(fmt.Sprintf("clusters/%s", cluster.ClusterId)))
				g.Expect(cluster.DisplayName).Should(Equal("get-test-2"))
				g.Expect(cluster.Version).ShouldNot(BeEmpty())
				g.Expect(cluster.UserPrefix).ShouldNot(BeEmpty())
				g.Expect(cluster.AutomatedBackupPolicy).ShouldNot(BeNil())
				g.Expect(cluster.Endpoints).ShouldNot(BeNil())
				g.Expect(cluster.SpendingLimit).ShouldNot(BeNil())
				g.Expect(cluster.GetRegion().GetDisplayName()).
					Should(Equal(fakeRegionDisplayName(cluster.GetRegion().GetRegionId())))
			},
		},
	}

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

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

	cluster := &entity.Cluster{
		ClusterId:      123,
		ProjectId:      456,
		OrganizationId: 111,
		DisplayName:    "essential-cluster",
		DeployType:     entity.DeployTypeServerless,
		CloudProvider:  openapicommonv1beta1.Region_aws,
		Region:         "us-east-1",
		GlobalCluster: &globalserverlessv1.ServerlessServiceCluster{
			ClusterId: "123",
		},
		ServicePlan: openapicommonv1beta1.ServicePlan_Essential,
		EngineType:  openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}

	result, err := buildClusterFromEntity(cluster, nil, openapiserverlessv1beta1.ClusterView_BASIC)
	if err != nil {
		t.Fatalf("buildClusterFromEntity() unexpected error: %v", err)
	}
	if result.GetServicePlan() != openapicommonv1beta1.ServicePlan_Essential {
		t.Errorf("ServicePlan = %v, want %v", result.GetServicePlan(), openapicommonv1beta1.ServicePlan_Essential)
	}
	if result.GetEngineType() != openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE {
		t.Errorf("EngineType = %v, want %v", result.GetEngineType(), openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE)
	}
}
