package ngoperation

import (
	"context"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"go.uber.org/mock/gomock"

	clusterng "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/infra/clients/coresvccli"
	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

var _ = Describe("CreatePlacementPolicy", func() {
	const (
		clusterID     = "1001"
		sharedPoolID  = "2002"
		tikvGroupID   = "tikv-dedicated"
		tenantRegion  = "us-west-2"
		cloudProvider = "aws"
	)

	baseInstance := func(params map[opent.ParamName]string) *opent.Instance {
		instance := &opent.Instance{
			Params: map[opent.ParamName]string{
				opent.ParamClusterID:      clusterID,
				opent.ParamResourcePoolID: sharedPoolID,
				opent.ParamTiKVGroupID:    tikvGroupID,
			},
		}
		for k, v := range params {
			instance.Params[k] = v
		}
		return instance
	}

	resourcePool := func() *nextgenv1.ResourcePoolProfile {
		return &nextgenv1.ResourcePoolProfile{
			ResourcePool: &nextgenv1.ResourcePool{
				Id: 2002,
			},
			Region: &commonv1.Region{
				LogicalName:     tenantRegion,
				CloudProviderId: commonv1.CloudProvider_AWS,
			},
		}
	}

	Describe("buildCreatePlacementPolicyParam", func() {
		It("builds the create request from cluster-scoped params", func() {
			req, err := buildCreatePlacementPolicyParam(baseInstance(nil))

			Expect(err).NotTo(HaveOccurred())
			Expect(req.SharedPoolId).To(Equal(sharedPoolID))
			Expect(req.ClusterId).To(Equal(clusterID))
			Expect(req.TikvGroupId).To(Equal(tikvGroupID))
		})

		It("requires tikv_group_id", func() {
			instance := baseInstance(nil)
			delete(instance.Params, opent.ParamTiKVGroupID)

			_, err := buildCreatePlacementPolicyParam(instance)

			Expect(err).To(MatchError(ContainSubstring("tikv_group_id is required")))
		})
	})

	Describe("Start", func() {
		It("creates a placement policy through the shared pool regional server", func() {
			ctrl := gomock.NewController(GinkgoT())
			clusterClient := coresvccli.NewMockClusterServiceClient(ctrl)
			clusterClient.EXPECT().ListClusters(gomock.Any(), gomock.Any()).Return(&mgmtv1.ListClustersResponse{
				Clusters: []*mgmtv1.Cluster{{
					ClusterId:  1001,
					DeployType: mgmtv1.DeployType_DEPLOY_TYPE_NEXTGEN,
					ClusterInfo: &mgmtv1.Cluster_NextGenCluster{
						NextGenCluster: &nextgenv1.Cluster{
							SharedPoolId: sharedPoolID,
						},
					},
				}},
			}, nil)

			regional := mgmthttpcli.NewMockNextGenRegionServer(ctrl)
			regional.EXPECT().CreatePlacementPolicy(gomock.Any(), gomock.Any()).DoAndReturn(
				func(_ context.Context, req *clusterng.CreatePlacementPolicyRequest) (*clusterng.PlacementPolicy, error) {
					Expect(req.SharedPoolId).To(Equal(sharedPoolID))
					Expect(req.ClusterId).To(Equal(clusterID))
					Expect(req.TikvGroupId).To(Equal(tikvGroupID))
					return &clusterng.PlacementPolicy{
						SharedPoolId:      sharedPoolID,
						PlacementPolicyId: clusterID,
						ClusterId:         clusterID,
						TikvGroupId:       tikvGroupID,
						Phase:             clusterng.PlacementPolicy_AVAILABLE,
					}, nil
				})

			mgmtHTTP := mgmthttpcli.NewMockMgmtHTTPClient(ctrl)
			mgmtHTTP.EXPECT().NextGenRegionServer(tenantRegion, cloudProvider).Return(regional)

			action := &createPlacementPolicy{comps: &actions.Components{
				MgmtHTTP: mgmtHTTP,
				CoreSvc: &coresvccli.CoreSvcCli{
					Mgmt: &coresvccli.MgmtService{
						Cluster:      clusterClient,
						ResourcePool: &fakeResourcePoolServiceClient{resourcePool: resourcePool()},
					},
				},
			}}

			result, err := action.Start(context.Background(), baseInstance(nil))

			Expect(err).NotTo(HaveOccurred())
			Expect(result.Result).To(Equal(opent.ActionStatusRunning))
			Expect(result.InstanceOutputs).To(HaveKeyWithValue(opent.OutputPlacementPolicyID, clusterID))
		})
	})
})
