package cluster_svc

import (
	"context"
	"encoding/json"
	"fmt"
	"slices"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/clusterproxy"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/common"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/console"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	"github.com/tidbcloud/serverless-service/internal/errorcode"
	"github.com/tidbcloud/serverless-service/internal/utils"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
	"go.uber.org/zap"
)

const (
	ChangefeedFeatureName            = "changefeed"
	PrivateLinkConnectionFeatureName = "private link connection"
	DatabaseAuditLogFeatureName      = "database audit log"
)

const (
	NotEssentialPlanMessage = "The %s feature is only available for essential plan. Please upgrade your plan."
	FeatureGateForbiddenMsg = "The %s feature is upon request and unavailable for your organization. Please contact support to request access."
	RegionNotSupportedMsg   = "The %s feature is currently unavailable in the %s region. Please contact support for assistance."
)

type clusterConsoleService struct {
	clusterProxyProviderFacade facade.ClusterProxyProviderFacade
	accountFacade              facade.AccountFacade
	o11yFacade                 facade.O11yFacade
	encryptorFacade            facade.EncryptorFacade
	billingFacade              facade.BillingFacade
	mgmtFacade                 facade.MgmtSvcFacade
	featuregate                facade.FeatureGate
	console.UnimplementedClusterServiceServer
}

func NewClusterConsoleService(
	clusterProxyProviderFacade facade.ClusterProxyProviderFacade,
	accountFacade facade.AccountFacade,
	o11yFacade facade.O11yFacade,
	encryptorFacade facade.EncryptorFacade,
	billingFacade facade.BillingFacade,
	mgmtFacade facade.MgmtSvcFacade,
	featuregate facade.FeatureGate,
) console.ClusterServiceServer {
	return &clusterConsoleService{
		clusterProxyProviderFacade: clusterProxyProviderFacade,
		accountFacade:              accountFacade,
		o11yFacade:                 o11yFacade,
		encryptorFacade:            encryptorFacade,
		billingFacade:              billingFacade,
		mgmtFacade:                 mgmtFacade,
		featuregate:                featuregate,
	}
}

func (s *clusterConsoleService) ListDatabases(
	ctx context.Context,
	req *console.ClusterListDatabasesReq,
) (_ *console.ClusterListDatabasesResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.ListDatabases")
	defer func() { tracing.EndSpan(span, bizerror) }()

	user, err := utils.GetUserFromContext(ctx)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "user not found")
	}
	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId)).With(zap.Uint64("user_id", user.ID))
	span.SetAttributes(
		attribute.String("cluster_id", fmt.Sprint(req.ClusterId)),
		attribute.String("user_id", fmt.Sprint(user.ID)),
	)

	span.AddEvent("get JWT")
	jwt, bizerror := s.accountFacade.GetJwtByUser(ctx, user.Email, user.ID, req.ClusterId)
	if bizerror != nil {
		logger.Error("failed to get jwt", zap.Error(bizerror))
		return nil, bizerror
	}

	span.AddEvent("get cluster")
	cluster, bizerror := s.mgmtFacade.GetCluster(ctx, req.ClusterId, tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC)
	if bizerror != nil {
		logger.Error("failed to get cluster", zap.Error(bizerror))
		return nil, bizerror
	}
	if cluster.GetServerlessCluster().
		GetUserStatus() !=
		globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL {
		return nil, bizerr.New(errorcode.ErrCodeClusterNotRunning, "cluster is not running")
	}

	logger = logger.With(zap.String("region", cluster.Region))
	span.AddEvent("get cluster proxy")
	operator := &clusterproxy.ClusterOperator{
		Username:   jwt.UserName,
		AuthMethod: clusterproxy.AuthMethodToken,
		Credential: jwt.Jwt,
	}

	proxy, err := s.clusterProxyProviderFacade.GetClusterProxy(ctx, cluster, operator)
	if err != nil {
		logger.Error("failed to get cluster proxy", zap.Error(err))
		return nil, bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to get cluster proxy")
	}

	span.AddEvent("list databases sql")
	res, err := proxy.QuerySQL(ctx, "SHOW DATABASES")
	if err != nil {
		logger.Error("failed to query sql", zap.Error(err))
		return nil, err
	}

	type DatabaseResult struct {
		Databases [][]byte `json:"Database"`
	}
	result := DatabaseResult{}
	err = json.Unmarshal(res, &result)
	if err != nil {
		logger.Error("failed to unmarshal json", zap.Error(err))
		return nil, bizerr.New(bizerr.ErrCodeJsonMarshal, "failed to unmarshal json")
	}
	var databases []string
	for _, database := range result.Databases {
		if !slices.Contains(constant.ExcludeDatabases, string(database)) {
			databases = append(databases, string(database))
		}
	}

	return &console.ClusterListDatabasesResp{
		Databases: databases,
		BaseResp:  &common.BaseResp{},
	}, nil
}

func (s *clusterConsoleService) CreateDatabase(
	ctx context.Context,
	req *console.ClusterCreateDatabaseReq,
) (resp *console.ClusterCreateDatabaseResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.CreateDatabase")
	defer func() { tracing.EndSpan(span, bizerror) }()

	user, err := utils.GetUserFromContext(ctx)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "user not found")
	}
	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId)).With(zap.Uint64("user_id", user.ID))
	span.SetAttributes(
		attribute.String("cluster_id", fmt.Sprint(req.ClusterId)),
		attribute.String("user_id", fmt.Sprint(user.ID)),
	)

	span.AddEvent("get JWT")
	jwt, bizerror := s.accountFacade.GetJwtByUser(ctx, user.Email, user.ID, req.ClusterId)
	if bizerror != nil {
		logger.Error("failed to get jwt", zap.Error(bizerror))
		return nil, bizerror
	}

	span.AddEvent("get cluster")
	cluster, bizerror := s.mgmtFacade.GetCluster(ctx, req.ClusterId, tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC)
	if bizerror != nil {
		logger.Error("failed to get cluster", zap.Error(bizerror))
		return nil, bizerror
	}
	if cluster.GetServerlessCluster().
		GetUserStatus() !=
		globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL {
		return nil, bizerr.New(errorcode.ErrCodeClusterNotRunning, "cluster is not running")
	}

	logger = logger.With(zap.String("region", cluster.Region))
	span.AddEvent("get cluster proxy")
	operator := &clusterproxy.ClusterOperator{
		Username:   jwt.UserName,
		AuthMethod: clusterproxy.AuthMethodToken,
		Credential: jwt.Jwt,
	}

	proxy, err := s.clusterProxyProviderFacade.GetClusterProxy(ctx, cluster, operator)
	if err != nil {
		logger.Error("failed to get cluster proxy", zap.Error(err))
		return nil, bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to get cluster proxy")
	}

	span.AddEvent("add databases sql")
	err = proxy.ExecuteSQL(ctx, fmt.Sprintf("CREATE DATABASE `%s`", utils.EscapeBackticks(req.Payload.DatabaseName)))
	if err != nil {
		logger.Error("failed to execute sql", zap.Error(err))
		return nil, errorcode.MarkClusterSQLErrorUserActionable(err)
	}

	return &console.ClusterCreateDatabaseResp{BaseResp: &common.BaseResp{}}, nil
}

func (s *clusterConsoleService) AdviseIndexes(
	ctx context.Context,
	req *console.ClusterAdviseIndexesReq,
) (resp *console.ClusterAdviseIndexesResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.AdviseIndexes")
	defer func() { tracing.EndSpan(span, bizerror) }()

	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId))

	advisorRepo, err := s.getIndexAdvisorRepo(ctx, span, logger, req.ClusterId, req.WithCloudAdmin)
	if err != nil {
		logger.Error("failed to get index advisor repo", zap.Error(err))
		return nil, err
	}
	ddls, err := advisorRepo.Advise(ctx, req.Payload.Queries)
	if err != nil {
		prepared := errorcode.PrepareErrorMessage(ctx, err, nil)
		return &console.ClusterAdviseIndexesResp{
			Text:     prepared.ErrMsg(),
			BaseResp: &common.BaseResp{},
		}, nil
	}
	logger.Info("advise index result successfully")
	if len(req.Payload.Queries) > 0 && len(ddls) == 0 { // empty result
		return &console.ClusterAdviseIndexesResp{
			Text:     "No index advice found",
			BaseResp: &common.BaseResp{},
		}, nil
	}

	// return nothing, have to use List to get the actual results.
	return &console.ClusterAdviseIndexesResp{
		Text:     strings.Join(ddls, ";"),
		BaseResp: &common.BaseResp{},
	}, nil
}

func (s *clusterConsoleService) ApplyIndexAdvice(
	ctx context.Context,
	req *console.ApplyIndexAdviceReq,
) (resp *console.ApplyIndexAdviceResp, bizerror error) {
	time.Sleep(10 * time.Second)
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.ApplyIndexAdvice")
	defer func() { tracing.EndSpan(span, bizerror) }()

	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId))

	span.AddEvent("get cluster")

	advisorRepo, err := s.getIndexAdvisorRepo(ctx, span, logger, req.ClusterId, true)
	if err != nil {
		logger.Error("failed to get index advisor repo", zap.Error(err))
		return nil, err
	}
	if err := advisorRepo.ApplyByID(ctx, req.Payload.AdviceId); err != nil {
		logger.Error("failed to apply index", zap.Error(err))
		return nil, err
	}

	return &console.ApplyIndexAdviceResp{BaseResp: &common.BaseResp{}}, nil
}

func (s *clusterConsoleService) CloseIndexAdvice(
	ctx context.Context,
	req *console.CloseIndexAdviceReq,
) (resp *console.CloseIndexAdviceResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.CloseIndexAdvice")
	defer func() { tracing.EndSpan(span, bizerror) }()

	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId))
	span.SetAttributes(attribute.String("cluster_id", fmt.Sprint(req.ClusterId)))

	advisorRepo, err := s.getIndexAdvisorRepo(ctx, span, logger, req.ClusterId, true)
	if err != nil {
		logger.Error("failed to get index advisor repo", zap.Error(err))
		return nil, err
	}
	if err := advisorRepo.UpdateStatusByID(ctx, req.Payload.AdviceId, STATECLOSED); err != nil {
		logger.Error("failed to close index", zap.Error(err))
		return nil, err
	}

	return &console.CloseIndexAdviceResp{BaseResp: &common.BaseResp{}}, nil
}

func (s *clusterConsoleService) ListIndexAdvices(
	ctx context.Context,
	req *console.ListIndexAdvicesReq,
) (resp *console.ListIndexAdvicesResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.ListIndexAdvices")
	defer func() { tracing.EndSpan(span, bizerror) }()

	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId))
	span.SetAttributes(
		attribute.String("cluster_id", fmt.Sprint(req.ClusterId)),
	)

	advisorRepo, err := s.getIndexAdvisorRepo(ctx, span, logger, req.ClusterId, true)
	if err != nil {
		logger.Error("failed to get index advisor repo", zap.Error(err))
		return nil, err
	}
	if err = advisorRepo.SyncApplyingAdvices(ctx); err != nil {
		logger.Error("failed to sync applying indexes", zap.Error(err))
	}
	filter := buildFilter(req)
	indexAdvisorResults, err := advisorRepo.List(ctx, filter)
	if err != nil {
		logger.Error("failed to list index advisor result", zap.Error(err))
		return nil, bizerr.New(bizerr.ErrCodeDB, "failed to list index advisor results")
	}

	total, err := advisorRepo.Count(ctx, filter)
	if err != nil {
		logger.Error("failed to count index advisor result", zap.Error(err))
		return nil, bizerr.New(bizerr.ErrCodeDB, "failed to count index advisor results")
	}

	advices := make([]*console.IndexAdvice, len(indexAdvisorResults))
	for i, result := range indexAdvisorResults {
		advices[i] = convertIndexAdvisorResult(result)
	}
	return &console.ListIndexAdvicesResp{
		Advices:  advices,
		Total:    uint32(total),
		BaseResp: &common.BaseResp{},
	}, nil
}

func (s *clusterConsoleService) GetIndexAdvice(
	ctx context.Context,
	req *console.GetIndexAdviceReq,
) (resp *console.GetIndexAdviceResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.GetIndexAdvice")
	defer func() { tracing.EndSpan(span, bizerror) }()

	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId), zap.Uint64("advice_id", req.AdviceId))
	span.SetAttributes(
		attribute.String("cluster_id", fmt.Sprint(req.ClusterId)),
		attribute.String("advice_id", fmt.Sprint(req.AdviceId)),
	)

	advisorRepo, err := s.getIndexAdvisorRepo(ctx, span, logger, req.ClusterId, true)
	if err != nil {
		logger.Error("failed to get index advisor repo", zap.Error(err))
		return nil, err
	}
	if err = advisorRepo.SyncApplyingAdvices(ctx); err != nil {
		logger.Error("failed to sync applying indexes", zap.Error(err))
	}
	indexAdvisorResult, err := advisorRepo.GetByID(ctx, req.AdviceId)
	if err != nil {
		logger.Error("failed to get index advisor result", zap.Error(err))
		return nil, bizerr.New(bizerr.ErrCodeDB, "failed to get index advisor result")
	}

	return &console.GetIndexAdviceResp{
		Advice:   convertIndexAdvisorResult(indexAdvisorResult),
		BaseResp: &common.BaseResp{},
	}, nil
}

func (s *clusterConsoleService) GetIndexAdviceSummary(
	ctx context.Context,
	req *console.GetIndexAdviceSummaryReq,
) (resp *console.GetIndexAdviceSummaryResp, bizerror error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.CreateDatabase")
	defer func() { tracing.EndSpan(span, bizerror) }()

	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", req.ClusterId))
	span.SetAttributes(
		attribute.String("cluster_id", fmt.Sprint(req.ClusterId)),
	)

	advisorRepo, err := s.getIndexAdvisorRepo(ctx, span, logger, req.ClusterId, false)
	if err != nil {
		logger.Error("failed to get index advisor repo", zap.Error(err))
		return nil, err
	}

	span.AddEvent("ListIndexAdvisorResults")
	indexAdvisorResults, err := advisorRepo.List(ctx, &IndexAdvisorFilter{State: "open"})
	if err != nil {
		logger.Error("failed to list index advisor result", zap.Error(err))
		return nil, bizerr.New(bizerr.ErrCodeDB, "failed to list index advisor results")
	}

	var improvement, costSavingMonthly float64
	for _, advice := range indexAdvisorResults {
		if advice.WorkloadImpact.WorkloadImprovement > improvement {
			improvement = advice.WorkloadImpact.WorkloadImprovement
		}
		// TODO: costSavingMonthly += advice.WorkloadCostSavingPerMon
	}

	return &console.GetIndexAdviceSummaryResp{
		Improvement:       roundFloat(improvement, 4),
		CostSavingMonthly: roundFloat(costSavingMonthly, 2),
		OpenCount:         uint32(len(indexAdvisorResults)),
		BaseResp:          &common.BaseResp{},
	}, nil
}

func (s *clusterConsoleService) getIndexAdvisorRepo(ctx context.Context, span trace.Span, logger *zap.Logger,
	clusterID uint64, withAdmin bool,
) (indexAdvisorRepo, error) {
	span.AddEvent("get cluster")
	cluster, bizerror := s.mgmtFacade.GetCluster(ctx, clusterID, tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC)
	if bizerror != nil {
		logger.Error("failed to get cluster", zap.Error(bizerror))
		return nil, bizerror
	}
	if cluster.GetServerlessCluster().
		GetUserStatus() !=
		globalserverlessv1.UserClusterStatus_USER_CLUSTER_STATUS_NORMAL {
		return nil, bizerr.New(errorcode.ErrCodeClusterNotRunning, "cluster is not running")
	}

	var operator *clusterproxy.ClusterOperator
	if withAdmin {
		logger.Info("run advisor with cloud admin")
		passwd, bizErr := s.accountFacade.GetCloudAdminPasswd(ctx, cluster.ClusterId)
		if bizErr != nil {
			logger.Error("get cloud admin password error", zap.Error(bizErr))
			return nil, bizerr.New(errorcode.ErrCodeInternalError, errorcode.InternalErrorMessage)
		}
		password, err := s.encryptorFacade.DecryptBase64(passwd)
		if err != nil {
			logger.Error("decrypt password error", zap.Error(err))
			return nil, bizerr.New(errorcode.ErrCodeInternalError, errorcode.InternalErrorMessage)
		}
		operator = &clusterproxy.ClusterOperator{
			Username:   constant.CloudAdminUsername,
			AuthMethod: clusterproxy.AuthMethodPassword,
			Credential: password,
		}
	} else {
		user, err := utils.GetUserFromContext(ctx)
		if err != nil {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "user not found")
		}
		span.SetAttributes(
			attribute.String("cluster_id", fmt.Sprint(clusterID)),
			attribute.String("user_id", fmt.Sprint(user.ID)),
		)
		span.AddEvent("get JWT")
		jwt, bizerror := s.accountFacade.GetJwtByUser(ctx, user.Email, user.ID, clusterID)
		if bizerror != nil {
			logger.Error("failed to get jwt", zap.Error(bizerror))
			return nil, bizerror
		}
		logger = logger.With(zap.String("region", cluster.Region))
		operator = &clusterproxy.ClusterOperator{
			Username:   jwt.UserName,
			AuthMethod: clusterproxy.AuthMethodToken,
			Credential: jwt.Jwt,
		}
	}

	span.AddEvent("get cluster proxy")
	proxy, err := s.clusterProxyProviderFacade.GetClusterProxy(ctx, cluster, operator)
	if err != nil {
		logger.Error("failed to get cluster proxy", zap.Error(err))
		return nil, bizerr.New(errorcode.ErrCodeSvcClusterProxy, "failed to get cluster proxy")
	}

	return &indexAdvisorRepoImpl{proxy: proxy, logger: logger}, nil
}

func (s *clusterConsoleService) CheckFeature(
	ctx context.Context,
	req *console.CheckFeatureReq,
) (resp *console.CheckFeatureResp, err error) {
	ctx, span := Tracer.Start(ctx, "clusterConsoleService.CheckFeature")
	defer func() { tracing.EndSpan(span, err) }()

	logger := log.WithContextL(ctx).With(zap.String("cluster_id", req.ClusterId))
	span.SetAttributes(attribute.String("cluster_id", req.ClusterId))

	clusterIDUint64, err := strconv.ParseUint(req.ClusterId, 10, 64)
	if err != nil {
		logger.Error("failed to parse cluster id", zap.Error(err))
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "")
	}
	cluster, err := s.mgmtFacade.GetCluster(ctx, clusterIDUint64, tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC)
	if err != nil {
		logger.Error("failed to get cluster", zap.Error(err))
		return nil, err
	}

	defer func() {
		if err == nil {
			resp = &console.CheckFeatureResp{Success: true}
		}
	}()

	switch req.FeatureName {
	case console.FeatureName_CHANGEFEED:
		return nil, ChangefeedFeatureGate(ctx, cluster)
	case console.FeatureName_PRIVATE_LINK_CONNECTION:
		return nil, PrivateLinkConnectionFeatureGate(ctx, cluster)
	case console.FeatureName_DATABASE_AUDIT_LOG:
		return nil, DatabaseAuditLogFeatureGate(ctx, cluster, s.featuregate)
	default:
		return nil, nil
	}
}

func ChangefeedFeatureGate(ctx context.Context, cluster *tidbmgmtservicev1.Cluster) bizerr.BizError {
	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", cluster.ClusterId))
	logger.Info("changefeed feature gate")
	if config.Stage() == config.StageDev {
		return nil
	}

	if cluster.GetServerlessCluster().GetCluster().GetAutoScaling().GetMinRcu() == 0 ||
		cluster.GetServerlessCluster().GetCluster().GetAutoScaling().GetMaxRcu() == 0 {
		return bizerr.New(bizerr.ErrCodeParams, NotEssentialPlanMessage, ChangefeedFeatureName)
	}

	return nil
}

func PrivateLinkConnectionFeatureGate(ctx context.Context, cluster *tidbmgmtservicev1.Cluster) bizerr.BizError {
	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", cluster.ClusterId))
	logger.Info("private link connection feature gate")
	if config.Stage() == config.StageDev {
		return nil
	}

	if cluster.GetServerlessCluster().GetCluster().GetAutoScaling().GetMinRcu() == 0 ||
		cluster.GetServerlessCluster().GetCluster().GetAutoScaling().GetMaxRcu() == 0 {
		return bizerr.New(bizerr.ErrCodeParams, NotEssentialPlanMessage, PrivateLinkConnectionFeatureName)
	}

	return nil
}

func DatabaseAuditLogFeatureGate(
	ctx context.Context,
	cluster *tidbmgmtservicev1.Cluster,
	gate facade.FeatureGate,
) bizerr.BizError {
	logger := log.WithContextL(ctx).With(zap.Uint64("cluster_id", cluster.ClusterId))
	logger.Info("database audit log feature gate")
	if config.Stage() == config.StageDev {
		return nil
	}

	if !gate.FeatureEnabled(constant.FeatureDatabaseAuditLog, cluster.OrganizationId, cluster.Region) {
		return bizerr.New(bizerr.ErrCodeParams, FeatureGateForbiddenMsg, DatabaseAuditLogFeatureName)
	}

	// enable aeterlink for starter plan.
	if cluster.OrganizationId == 1372813089209230546 {
		return nil
	}
	// enable pingcap-prod for starter plan, this is our internal org.
	if cluster.OrganizationId == 1372813089187041280 {
		return nil
	}
	if cluster.GetServerlessCluster().GetCluster().GetAutoScaling().GetMinRcu() == 0 ||
		cluster.GetServerlessCluster().GetCluster().GetAutoScaling().GetMaxRcu() == 0 {
		return bizerr.New(bizerr.ErrCodeParams, NotEssentialPlanMessage, DatabaseAuditLogFeatureName)
	}
	return nil
}
