package action

import (
	"errors"
	"fmt"

	"github.com/tidbcloud/fastflow/pkg/entity/run"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/protobuf/encoding/protojson"

	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/adapter"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

const (
	PreCreateCluster = "pre_create_cluster"
)

type PreCreateClusterParameter struct {
	ClusterId   string `json:"cluster_id"`
	RequestBody string `json:"request_body"`
}

type PreCreateClusterAction struct {
	clusterRepo      repository.ClusterRepo
	serverlessFacade adapter.ServerlessService
	brFacade         adapter.BRService
}

func NewPreCreateClusterAction(
	clusterRepo repository.ClusterRepo,
	serverlessFacade adapter.ServerlessService,
	brFacade adapter.BRService,
) *PreCreateClusterAction {
	return &PreCreateClusterAction{
		clusterRepo:      clusterRepo,
		serverlessFacade: serverlessFacade,
		brFacade:         brFacade,
	}
}

func (a *PreCreateClusterAction) Name() string {
	return PreCreateCluster
}

func (a *PreCreateClusterAction) ParameterNew() any {
	return &PreCreateClusterParameter{}
}

func (a *PreCreateClusterAction) Run(ctx run.ExecuteContext, params any) error {
	cctx, span := workflow.GenerateContext(ctx)

	createClusterParam, ok := params.(*PreCreateClusterParameter)
	if !ok {
		tilog.Error(cctx, "expect PreCreateClusterParameter parameter")
		return errors.New("expect PreCreateClusterParameter parameter")
	}

	var err error
	tilog.Info(cctx, "start to pre create cluster", zap.String("cluster_id", createClusterParam.ClusterId))
	defer func() {
		if err != nil {
			tilog.Error(
				cctx,
				"failed to pre create cluster",
				zap.String("cluster_id", createClusterParam.ClusterId),
				zap.Error(err),
			)
		} else {
			tilog.Info(cctx, "end to pre create cluster", zap.String("cluster_id", createClusterParam.ClusterId))
		}
		span.End()
	}()

	var req globalserverlessv1.ServerlessServiceCreateRequest
	if err := protojson.Unmarshal([]byte(createClusterParam.RequestBody), &req); err != nil {
		return err
	}

	var clusterEn *entity.Cluster
	clusterEn, err = a.clusterRepo.Get(cctx, createClusterParam.ClusterId, true)
	if err != nil {
		return err
	}

	if !clusterEn.DeletedAt.IsZero() {
		tilog.Warn(
			cctx,
			"the cluster is deleted, interrupt initialize task",
			zap.String("cluster", createClusterParam.ClusterId),
		)
		return nil
	}

	// spend limit 0 is already set
	if req.GetSpendLimit().GetMonthly() != 0 {
		if err = a.serverlessFacade.SetSpendLimit(
			cctx,
			clusterEn.ClusterId,
			clusterEn.TenantId,
			req.GetSpendLimit().GetMonthly(),
			clusterEn.Region.Id.String(),
			req.GetEncryptionConfig().GetEnhancedEncryptionEnabled(),
			false,
		); err != nil {
			return fmt.Errorf("set spendLimit failed: %w", err)
		}
	}

	if req.GetBackupPolicy() != nil {
		if err := a.brFacade.SetBackupPolicy(cctx, clusterEn.ClusterId, clusterEn.TenantId, clusterEn.ProjectId, req.GetBackupPolicy()); err != nil {
			return fmt.Errorf("set backup policy failed: %w", err)
		}
	}

	return err
}
