package action

import (
	"errors"
	"strings"

	"github.com/tidbcloud/fastflow/pkg/entity/run"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/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 (
	DeleteCluster = "delete_cluster"
)

type DeleteClusterParameter struct {
	ClusterId string `json:"cluster_id"`
}

type DeleteClusterAction struct {
	clusterRepo      repository.ClusterRepo
	regionalService  adapter.RegionalService
	serverlessFacade adapter.ServerlessService
	sys9Lifecycle    adapter.Sys9Lifecycle
}

func NewDeleteClusterAction(
	clusterRepo repository.ClusterRepo,
	regionalService adapter.RegionalService,
	serverlessFacade adapter.ServerlessService,
	sys9Lifecycle adapter.Sys9Lifecycle,
) *DeleteClusterAction {
	return &DeleteClusterAction{
		clusterRepo:      clusterRepo,
		regionalService:  regionalService,
		serverlessFacade: serverlessFacade,
		sys9Lifecycle:    sys9Lifecycle,
	}
}

func (a *DeleteClusterAction) Name() string {
	return DeleteCluster
}

func (a *DeleteClusterAction) ParameterNew() any {
	return &DeleteClusterParameter{}
}

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

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

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

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

	err = a.serverlessFacade.DeleteSpendLimit(cctx, clusterEn.ClusterId, clusterEn.TenantId)
	if err != nil {
		return err
	}

	if isPGStarterClusterForDelete(clusterEn) {
		err = a.sys9Lifecycle.DeleteCluster(cctx, clusterEn, clusterEn.ExternalInstanceID)
		if err != nil {
			return err
		}
		return nil
	}

	err = a.regionalService.DeleteCluster(cctx, clusterEn.Region, clusterEn.ClusterId, clusterEn.TenantId, clusterEn.ProjectId)
	if err != nil {
		return err
	}

	return err
}

func isPGStarterClusterForDelete(cluster *entity.Cluster) bool {
	if cluster == nil || cluster.Region == nil {
		return false
	}
	if cluster.ServicePlan != globalcommonv1.ServicePlan_Starter {
		return false
	}
	return strings.EqualFold(cluster.EngineType, "POSTGRESQL_COMPATIBLE")
}
