package facade_imp

import (
	"context"
	"fmt"
	"slices"
	"time"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	"github.com/tidbcloud/serverless-service/internal/grpc/grpcli"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"google.golang.org/protobuf/proto"
	"k8s.io/utils/pointer"
)

type mgmtSvcFacade struct{}

func NewMgmtSvcFacade() facade.MgmtSvcFacade {
	return &mgmtSvcFacade{}
}

func (f *mgmtSvcFacade) GetCluster(
	ctx context.Context,
	clusterId uint64,
	view tidbmgmtservicev1.ClusterFilter_ClusterView,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:    []uint64{clusterId},
			ExcludeStates: facade.DeletedClusterStatus,
			View:          view,
			ClusterType:   tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
		},
	}
	listResp, err := grpcli.ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(listResp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return listResp.Clusters[0], nil
}

func (f *mgmtSvcFacade) GetClusterIncludeDedicated(
	ctx context.Context,
	clusterId uint64,
	view tidbmgmtservicev1.ClusterFilter_ClusterView,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:    []uint64{clusterId},
			ExcludeStates: facade.DeletedClusterStatus,
			View:          view,
		},
	}
	listResp, err := grpcli.ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(listResp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return listResp.Clusters[0], nil
}

func (f *mgmtSvcFacade) GetClusterIncludeDeleted(
	ctx context.Context,
	clusterId uint64,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:  []uint64{clusterId},
			ClusterType: tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
			Deleted:     pointer.Bool(true),
		},
	}
	listResp, err := grpcli.ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(listResp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return listResp.Clusters[0], nil
}

func (f *mgmtSvcFacade) ListClusters(
	ctx context.Context,
	req *tidbmgmtservicev1.ListClustersRequest,
) (*tidbmgmtservicev1.ListClustersResponse, bizerr.BizError) {
	listResp, err := grpcli.ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	return listResp, nil
}

func (f *mgmtSvcFacade) ListAllClusters(
	ctx context.Context,
	req *tidbmgmtservicev1.ListClustersRequest,
) ([]*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	if req == nil {
		req = &tidbmgmtservicev1.ListClustersRequest{}
	}
	if req.PageSize == 0 {
		req.PageSize = 100
	}
	if req.Page == 0 {
		req.Page = 1
	}
	var allClusters []*tidbmgmtservicev1.Cluster
	for {
		resp, err := f.ListClusters(ctx, req)
		if err != nil {
			return nil, err
		}
		allClusters = append(allClusters, resp.Clusters...)
		if len(resp.Clusters) < int(req.PageSize) {
			break
		}
		req.Page++
	}
	return allClusters, nil
}

func (f *mgmtSvcFacade) ListAllClustersInOrg(
	ctx context.Context,
	orgId uint64,
	view tidbmgmtservicev1.ClusterFilter_ClusterView,
) ([]*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			TenantIds:     []uint64{orgId},
			ExcludeStates: facade.DeletedClusterStatus,
			View:          view,
			ClusterType:   tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
		},
	}
	return f.ListAllClusters(ctx, req)
}

func (f *mgmtSvcFacade) ListDeletedClustersInOrg(
	ctx context.Context,
	orgId uint64,
	view tidbmgmtservicev1.ClusterFilter_ClusterView,
) ([]*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			TenantIds:   []uint64{orgId},
			View:        view,
			ClusterType: tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
			Deleted:     proto.Bool(true),
		},
	}
	clusters, err := f.ListAllClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	return clusters, nil
}

func (f *mgmtSvcFacade) SetClusterRestoreInPlaceStatus(
	ctx context.Context,
	clusterId uint64,
	restoreId string,
	timeout time.Duration,
) (uint64, bizerr.BizError) {
	timeoutSec := int64(timeout.Seconds())
	displayStatus := openapicommonv1beta1.ClusterState_RESTORING
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_INPLACE,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				StatusType: []tidbmgmtservicev1.CustomStatusType{
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_IMPORT,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_INPLACE,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_TO_NEW,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_MODIFYING,
				},
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_MODIFY_CLUSTER,
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_SET_ROOT_PASSWORD,
				},
			},
			TimeoutSecs:    &timeoutSec,
			OverrideStatus: &displayStatus,
			StatusName:     restoreId,
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

// SetClusterRestoreToNewStatus need not set displayStatus, because the underlying status already set to recover when
// restore to new cluster, and we need not to mutex CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_TO_NEW
func (f *mgmtSvcFacade) SetClusterRestoreToNewStatus(
	ctx context.Context,
	clusterId uint64,
	restoreId string,
) (uint64, bizerr.BizError) {
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_TO_NEW,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				StatusType: []tidbmgmtservicev1.CustomStatusType{
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_IMPORT,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_INPLACE,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_MODIFYING,
				},
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_MODIFY_CLUSTER,
				},
			},
			StatusName: restoreId,
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

// SetClusterCDCStatus sets the cluster custom status for CDC. It will forbid import, restore in place, and delete cluster actions
func (f *mgmtSvcFacade) SetClusterCDCStatus(ctx context.Context, clusterId uint64) (uint64, bizerr.BizError) {
	now := time.Now().UTC().Unix()
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				StatusType: []tidbmgmtservicev1.CustomStatusType{
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_IMPORT,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_INPLACE,
				},
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
				},
			},
			// why add now to the status name?
			// mgmt return conflict because of the same status name even you delete the previous one.
			StatusName: fmt.Sprintf("cdc-%d-%d", clusterId, now),
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

func (f *mgmtSvcFacade) RegisterCDCClusterJob(ctx context.Context, clusterId uint64, jobId string) bizerr.BizError {
	// 1. Check if it already exists for idempotency (Harden)
	getReq := &tidbmgmtservicev1.GetClusterCustomStatusByNameRequest{
		ClusterId:  clusterId,
		StatusName: jobId,
		StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
	}
	_, err := grpcli.GetClusterCustomStatusByName(ctx, getReq)
	if err == nil {
		// Already exists, return success
		return nil
	} else if err.ErrCode() != bizerr.ErrCodeRecordNotFound {
		// Return real error if it's not simply "not found"
		return err
	}

	// 2. Register it with empty StatusType (Copy dataflow-service-ng)
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				StatusType: []tidbmgmtservicev1.CustomStatusType{},
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_PAUSE_CLUSTER,
				},
			},
			StatusName: jobId,
		},
	}
	_, err = grpcli.SetClusterCustomStatus(ctx, req)
	return err
}

func (f *mgmtSvcFacade) DeregisterCDCClusterJob(ctx context.Context, clusterId uint64, jobId string) bizerr.BizError {
	req := &tidbmgmtservicev1.GetClusterCustomStatusByNameRequest{
		ClusterId:  clusterId,
		StatusName: jobId,
		StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
	}
	status, err := grpcli.GetClusterCustomStatusByName(ctx, req)
	if err != nil {
		if err.ErrCode() == bizerr.ErrCodeRecordNotFound {
			return nil
		}
		return err
	}
	if status == nil {
		return nil
	}
	return f.DeleteClusterCustomStatus(ctx, clusterId, status.GetStatusId())
}

// isLegacyCDCClusterLock reports whether the given status is the old-arch cluster-level
// CDC lock: a CDC-typed status whose own mutex still lists CUSTOM_STATUS_TYPE_CDC. That
// self-mutex is what blocks newarch per-changefeed job registration. Newarch per-changefeed
// jobs are also CDC-typed but carry an empty status mutex, so they are excluded here.
func isLegacyCDCClusterLock(s *tidbmgmtservicev1.ClusterCustomStatus) bool {
	if s.GetStatusType() != tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC {
		return false
	}
	if s.GetIsExpired() {
		return false
	}
	return slices.Contains(
		s.GetMutex().GetStatusType(),
		tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
	)
}

func (f *mgmtSvcFacade) ReplaceLegacyCDCClusterLock(
	ctx context.Context,
	clusterId uint64,
) (replaced bool, oldStatusId, newStatusId uint64, err bizerr.BizError) {
	listResp, err := grpcli.ListClusterCustomStatus(ctx, &tidbmgmtservicev1.ListClusterCustomStatusRequest{
		ClusterIds: []uint64{clusterId},
		StatusTypes: []tidbmgmtservicev1.CustomStatusType{
			tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
		},
	})
	if err != nil {
		return false, 0, 0, err
	}

	var legacy *tidbmgmtservicev1.ClusterCustomStatus
	for _, s := range listResp.GetCustomStatuses() {
		if isLegacyCDCClusterLock(s) {
			legacy = s
			break
		}
	}
	if legacy == nil {
		// Nothing to heal: the forbidden error came from something other than the legacy
		// CDC self-mutex lock (e.g. a real import/restore in progress). Let the caller
		// surface the original error.
		return false, 0, 0, nil
	}

	oldStatusId = legacy.GetStatusId()

	// Rebuild the mutex from the legacy lock, dropping only the CDC self-mutex. The
	// IMPORT/RESTORE_INPLACE status mutex and the DELETE action mutex are preserved so the
	// lock keeps protecting any still-running old-arch changefeed on this cluster.
	replacementMutex := &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
		ClusterAction: legacy.GetMutex().GetClusterAction(),
	}
	for _, t := range legacy.GetMutex().GetStatusType() {
		if t == tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC {
			continue
		}
		replacementMutex.StatusType = append(replacementMutex.StatusType, t)
	}

	// There is no in-place update API, so delete the legacy lock and recreate an
	// equivalent one without the CDC self-mutex. NOTE: this leaves a two-RPC-wide window in
	// which import/restore/delete are unprotected for this cluster; acceptable for the
	// migration period.
	if delErr := grpcli.DeleteClusterCustomStatus(ctx, &tidbmgmtservicev1.DeleteClusterCustomStatusRequest{
		ClusterId: clusterId,
		StatusId:  oldStatusId,
	}); delErr != nil {
		return false, 0, 0, delErr
	}

	now := time.Now().UTC().Unix()
	created, err := grpcli.SetClusterCustomStatus(ctx, &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
			Mutex:      replacementMutex,
			StatusName: fmt.Sprintf("cdc-%d-%d", clusterId, now),
		},
	})
	if err != nil {
		return false, 0, 0, err
	}

	return true, oldStatusId, created.GetStatusId(), nil
}

func (f *mgmtSvcFacade) ListClusterCDCCustomStatuses(
	ctx context.Context,
	clusterId uint64,
) ([]*tidbmgmtservicev1.ClusterCustomStatus, bizerr.BizError) {
	resp, err := grpcli.ListClusterCustomStatus(ctx, &tidbmgmtservicev1.ListClusterCustomStatusRequest{
		ClusterIds: []uint64{clusterId},
		StatusTypes: []tidbmgmtservicev1.CustomStatusType{
			tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
		},
	})
	if err != nil {
		return nil, err
	}
	return resp.GetCustomStatuses(), nil
}

func (f *mgmtSvcFacade) SetClusterMigrationStatus(ctx context.Context, clusterId uint64) (uint64, bizerr.BizError) {
	now := time.Now().UTC().Unix()
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_MIGRATION,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				StatusType: []tidbmgmtservicev1.CustomStatusType{
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_IMPORT,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_INPLACE,
				},
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
				},
			},
			// why add now to the status name?
			// mgmt return conflict because of the same status name even you delete the previous one.
			StatusName: fmt.Sprintf("dm-%d-%d", clusterId, now),
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

func (f *mgmtSvcFacade) SetClusterImportStatus(ctx context.Context, clusterId uint64) (uint64, bizerr.BizError) {
	now := time.Now().UTC().Unix()
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_IMPORT,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				StatusType: []tidbmgmtservicev1.CustomStatusType{
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_MIGRATION,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_INPLACE,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
					tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE_TO_NEW,
				},
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
				},
			},
			// why add now to the status name?
			// mgmt return conflict because of the same status name even you delete the previous one.
			StatusName: fmt.Sprintf("import-%d-%d", clusterId, now),
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

func (f *mgmtSvcFacade) SetClusterExportStatus(
	ctx context.Context,
	clusterId uint64,
	exportId string,
) (uint64, bizerr.BizError) {
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_EXPORT,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
				},
			},
			StatusName: exportId,
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

func (f *mgmtSvcFacade) SetClusterPrivateLinkConnectionStatus(
	ctx context.Context,
	clusterId uint64,
	plcId string,
) (uint64, bizerr.BizError) {
	req := &tidbmgmtservicev1.SetClusterCustomStatusRequest{
		CustomStatus: &tidbmgmtservicev1.ClusterCustomStatus{
			ClusterId:  clusterId,
			StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_PRIVATE_LINK_CONNECTION_EXIST,
			Mutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
				ClusterAction: []tidbmgmtservicev1.ClusterAction{
					tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_DELETE_CLUSTER,
				},
			},
			StatusName: plcId,
		},
	}
	resp, err := grpcli.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return 0, err
	}
	return resp.StatusId, nil
}

func (f *mgmtSvcFacade) DeleteClusterCustomStatus(ctx context.Context, clusterId, statusId uint64) bizerr.BizError {
	req := &tidbmgmtservicev1.DeleteClusterCustomStatusRequest{
		ClusterId: clusterId,
		StatusId:  statusId,
	}
	return grpcli.DeleteClusterCustomStatus(ctx, req)
}

func (f *mgmtSvcFacade) GetClusterRestoreStatus(
	ctx context.Context,
	clusterId uint64,
	restoreId string,
) (uint64, bizerr.BizError) {
	req := &tidbmgmtservicev1.GetClusterCustomStatusByNameRequest{
		ClusterId:  clusterId,
		StatusName: restoreId,
		StatusType: tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_SERVERLESS_RESTORE,
	}
	status, err := grpcli.GetClusterCustomStatusByName(ctx, req)
	if err != nil {
		return 0, err
	}
	if status == nil {
		return 0, bizerr.New(bizerr.ErrCodeRecordNotFound, "record not find")
	}
	return status.GetStatusId(), nil
}

func (f *mgmtSvcFacade) RestoreNewCluster(
	ctx context.Context,
	req *tidbmgmtservicev1.RestoreServerlessClusterRequest,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	return grpcli.RestoreServerlessCluster(ctx, req)
}

func (f *mgmtSvcFacade) UpdateServerlessClusterAnnotations(ctx context.Context,
	clusterID uint64, annos map[string]string,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.UpdateServerlessRegionalClusterAnnotationsRequest{
		ClusterId:   clusterID,
		Annotations: annos,
	}
	return grpcli.UpdateServerlessRegionalClusterAnnotations(ctx, req)
}

func (f *mgmtSvcFacade) GetClusterBypassCache(
	ctx context.Context,
	clusterId uint64,
	view tidbmgmtservicev1.ClusterFilter_ClusterView,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:    []uint64{clusterId},
			ExcludeStates: facade.DeletedClusterStatus,
			View:          view,
			ClusterType:   tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
			RefreshCache:  true,
		},
	}
	listResp, err := grpcli.ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(listResp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return listResp.Clusters[0], nil
}

func (f *mgmtSvcFacade) Precheck(
	ctx context.Context,
	req *globalserverlessv1.PrecheckRequest,
) (*globalserverlessv1.PrecheckResponse, bizerr.BizError) {
	return grpcli.Precheck(ctx, req)
}

func (f *mgmtSvcFacade) CheckImportPermission(
	ctx context.Context,
	req *globalserverlessv1.CheckImportPermissionRequest,
) bizerr.BizError {
	return grpcli.CheckImportPermission(ctx, req)
}

func (f *mgmtSvcFacade) UpdateAuditLogConfig(
	ctx context.Context,
	clusterId string,
	req *regionalserverlessv1.ServerlessRegionalServiceClusterInfo_AuditLogConfig,
) bizerr.BizError {
	return grpcli.UpdateAuditLogConfig(ctx, clusterId, req)
}

func (f *mgmtSvcFacade) CheckCloudStoragePermission(
	ctx context.Context,
	req *globalserverlessv1.CheckCloudStoragePermissionRequest,
) bizerr.BizError {
	return grpcli.CheckCloudStoragePermission(ctx, req)
}

func (f *mgmtSvcFacade) DMPrecheck(
	ctx context.Context,
	req *globalserverlessv1.ServerlessDMServicePrecheckRequest,
) (*globalserverlessv1.ServerlessDMServicePrecheckResponse, bizerr.BizError) {
	return grpcli.DMPrecheck(ctx, req)
}

func (f *mgmtSvcFacade) CreateVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateVpcEndpointRequest,
) (*globalserverlessv1.CreateVpcEndpointResponse, bizerr.BizError) {
	return grpcli.CreateVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) DescribeVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeVpcEndpointRequest,
) (*globalserverlessv1.DescribeVpcEndpointResponse, bizerr.BizError) {
	return grpcli.DescribeVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) DeleteVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteVpcEndpointRequest,
) bizerr.BizError {
	return grpcli.DeleteVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) ListVpcEndpointZones(
	ctx context.Context,
	req *globalserverlessv1.ListVpcEndpointZonesRequest,
) (*globalserverlessv1.ListVpcEndpointZonesResponse, bizerr.BizError) {
	return grpcli.ListVpcEndpointZones(ctx, req)
}

func (f *mgmtSvcFacade) RemoveZoneFromVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.RemoveZoneFromVpcEndpointRequest,
) bizerr.BizError {
	return grpcli.RemoveZoneFromVpcEndpoint(ctx, req)
}

// ------- Private Link Connection -------
func (f *mgmtSvcFacade) CreateAWSVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateAWSVpcEndpointRequest,
) (*globalserverlessv1.CreateAWSVpcEndpointResponse, bizerr.BizError) {
	return grpcli.CreateAWSVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) DescribeAWSVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeAWSVpcEndpointRequest,
) (*globalserverlessv1.DescribeAWSVpcEndpointResponse, bizerr.BizError) {
	return grpcli.DescribeAWSVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) DeleteAWSVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteAWSVpcEndpointRequest,
) bizerr.BizError {
	return grpcli.DeleteAWSVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) CreateAliCloudVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateAliCloudVpcEndpointRequest,
) (*globalserverlessv1.CreateAliCloudVpcEndpointResponse, bizerr.BizError) {
	return grpcli.CreateAliCloudVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) DescribeAliCloudVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeAliCloudVpcEndpointRequest,
) (*globalserverlessv1.DescribeAliCloudVpcEndpointResponse, bizerr.BizError) {
	return grpcli.DescribeAliCloudVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) DeleteAliCloudVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteAliCloudVpcEndpointRequest,
) bizerr.BizError {
	return grpcli.DeleteAliCloudVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) GetZoneIds(
	ctx context.Context,
	req *globalserverlessv1.GetZoneIdsRequest,
) (*globalserverlessv1.GetZoneIdsResponse, bizerr.BizError) {
	return grpcli.GetZoneIds(ctx, req)
}

func (f *mgmtSvcFacade) ListAliCloudVpcEndpointZones(
	ctx context.Context,
	req *globalserverlessv1.ListAliCloudVpcEndpointZonesRequest,
) (*globalserverlessv1.ListAliCloudVpcEndpointZonesResponse, bizerr.BizError) {
	return grpcli.ListAliCloudVpcEndpointZones(ctx, req)
}

func (f *mgmtSvcFacade) RemoveAliCloudZoneFromVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.RemoveAliCloudZoneFromVpcEndpointRequest,
) bizerr.BizError {
	return grpcli.RemoveAliCloudZoneFromVpcEndpoint(ctx, req)
}

func (f *mgmtSvcFacade) CreateAliCloudDNSRecord(
	ctx context.Context,
	req *globalserverlessv1.CreateAliCloudDNSRecordRequest,
) (*globalserverlessv1.CreateAliCloudDNSRecordResponse, bizerr.BizError) {
	return grpcli.CreateAliCloudDNSRecord(ctx, req)
}

func (f *mgmtSvcFacade) DeleteAliCloudDNSRecord(
	ctx context.Context,
	req *globalserverlessv1.DeleteAliCloudDNSRecordRequest,
) bizerr.BizError {
	return grpcli.DeleteAliCloudDNSRecord(ctx, req)
}

func (f *mgmtSvcFacade) CreateAWSRoute53Records(
	ctx context.Context,
	req *globalserverlessv1.CreateAWSRoute53RecordsRequest,
) (*globalserverlessv1.CreateAWSRoute53RecordsResponse, bizerr.BizError) {
	return grpcli.CreateAWSRoute53Records(ctx, req)
}

func (f *mgmtSvcFacade) DeleteAWSRoute53Records(
	ctx context.Context,
	req *globalserverlessv1.DeleteAWSRoute53RecordsRequest,
) (*globalserverlessv1.DeleteAWSRoute53RecordsResponse, bizerr.BizError) {
	return grpcli.DeleteAWSRoute53Records(ctx, req)
}

func (f *mgmtSvcFacade) GetAWSRoute53Change(
	ctx context.Context,
	req *globalserverlessv1.GetAWSRoute53ChangeRequest,
) (*globalserverlessv1.GetAWSRoute53ChangeResponse, bizerr.BizError) {
	return grpcli.GetAWSRoute53Change(ctx, req)
}

func (f *mgmtSvcFacade) CreateAWSRoute53PrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.CreateAWSRoute53PrivateHostedZoneRequest,
) (*globalserverlessv1.CreateAWSRoute53PrivateHostedZoneResponse, bizerr.BizError) {
	return grpcli.CreateAWSRoute53PrivateHostedZone(ctx, req)
}

func (f *mgmtSvcFacade) DeleteAWSRoute53PrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.DeleteAWSRoute53PrivateHostedZoneRequest,
) (*globalserverlessv1.DeleteAWSRoute53PrivateHostedZoneResponse, bizerr.BizError) {
	return grpcli.DeleteAWSRoute53PrivateHostedZone(ctx, req)
}

func (f *mgmtSvcFacade) CreateAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.CreateAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.CreateAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	return grpcli.CreateAliCloudDNSPrivateHostedZone(ctx, req)
}

func (f *mgmtSvcFacade) DeleteAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.DeleteAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.DeleteAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	return grpcli.DeleteAliCloudDNSPrivateHostedZone(ctx, req)
}

func (f *mgmtSvcFacade) AttachVpcToAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.AttachVpcToAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.AttachVpcToAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	return grpcli.AttachVpcToAliCloudDNSPrivateHostedZone(ctx, req)
}

func (f *mgmtSvcFacade) DetachVpcFromAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.DetachVpcFromAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.DetachVpcFromAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	return grpcli.DetachVpcFromAliCloudDNSPrivateHostedZone(ctx, req)
}

func (f *mgmtSvcFacade) CreateMSKVPCConnection(
	ctx context.Context,
	req *globalserverlessv1.CreateMSKVPCConnectionRequest,
) (*globalserverlessv1.CreateMSKVPCConnectionResponse, bizerr.BizError) {
	return grpcli.CreateMSKVPCConnection(ctx, req)
}

func (f *mgmtSvcFacade) DeleteMSKVPCConnection(
	ctx context.Context,
	req *globalserverlessv1.DeleteMSKVPCConnectionRequest,
) (*globalserverlessv1.DeleteMSKVPCConnectionResponse, bizerr.BizError) {
	return grpcli.DeleteMSKVPCConnection(ctx, req)
}

func (f *mgmtSvcFacade) GetMSKVPCConnection(
	ctx context.Context,
	req *globalserverlessv1.GetMSKVPCConnectionRequest,
) (*globalserverlessv1.GetMSKVPCConnectionResponse, bizerr.BizError) {
	return grpcli.GetMSKVPCConnection(ctx, req)
}

// ------- Private Link Connection -------
