package action

import (
	"errors"

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

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/nextgenglobal"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/remoteencryptor"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/constant"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

type CreateNextGenClusterAction struct {
	nextgenCli  nextgenglobal.NextGenGlobal
	encryptor   remoteencryptor.Client
	clusterRepo repository.ClusterRepository
}

func NewCreateNextGenClusterAction(
	nextgenCli nextgenglobal.NextGenGlobal,
	encryptor remoteencryptor.Client,
	clusterRepo repository.ClusterRepository,
) *CreateNextGenClusterAction {
	return &CreateNextGenClusterAction{
		nextgenCli:  nextgenCli,
		encryptor:   encryptor,
		clusterRepo: clusterRepo,
	}
}

func (c *CreateNextGenClusterAction) Name() string {
	return constant.TaskNextGenGlobalCreateCluster
}

func (c *CreateNextGenClusterAction) Run(ctx run.ExecuteContext, params any) error {
	cctx, span := workflow.GenerateContext(ctx)
	defer span.End()

	tilog.Info(cctx, "create nextgen cluster action start")
	requestJson, ok := ctx.GetVar(constant.KeyRequest)
	if !ok {
		err := bizerr.New(bizerr.ErrCodeRecordNotFound, "create nextgen request is not found")
		tilog.Error(cctx, "create nextgen request is not found", zap.Error(err))
		return err
	}

	var request globalngv1.CreateClusterRequest
	if err := protojson.Unmarshal([]byte(requestJson), &request); err != nil {
		tilog.Error(cctx, "failed to unmarshal create nextgen request", zap.Error(err))
		return bizerr.WrapError(err, "unmarshal create nextgen request failed")
	}
	if request.GetCluster().GetClusterId() == 0 {
		err := bizerr.New(bizerr.ErrCodeParams, "cluster id is invalid")
		tilog.Error(cctx, "cluster id is invalid", zap.Error(err))
		return err
	}

	// decrypt root password if request
	if request.Cluster.RootPassword != "" {
		rootPassword, err := c.encryptor.DecryptBase64(cctx, request.Cluster.RootPassword)
		if err != nil {
			tilog.Error(cctx, "failed to decrypt root password", zap.Error(err))
			return bizerr.WrapError(err, "decrypt root password failed")
		}
		request.Cluster.RootPassword = rootPassword
	}

	request.ValidateOnly = false
	cluster, err := c.clusterRepo.GetCluster(cctx, request.GetCluster().GetClusterId(), true)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		err = workflow.NewTerminalError(
			bizerr.New(bizerr.ErrCodeRecordNotFound, "management cluster is missing or deleted"),
		)
		tilog.Error(cctx, "management cluster is missing or deleted",
			zap.Uint64("cluster_id", request.GetCluster().GetClusterId()), zap.Error(err))
		return err
	}
	if err != nil {
		err = bizerr.WrapError(err, "get management cluster failed")
		tilog.Error(cctx, "failed to get management cluster",
			zap.Uint64("cluster_id", request.GetCluster().GetClusterId()), zap.Error(err))
		return err
	}
	if cluster == nil || cluster.DeletedAt != nil {
		err = workflow.NewTerminalError(
			bizerr.New(bizerr.ErrCodeRecordNotFound, "management cluster is missing or deleted"),
		)
		tilog.Error(cctx, "management cluster is missing or deleted",
			zap.Uint64("cluster_id", request.GetCluster().GetClusterId()), zap.Error(err))
		return err
	}

	_, err = c.nextgenCli.CreateCluster(cctx, &request)
	if err != nil {
		err = bizerr.WrapError(err, "create nextgen cluster failed")
		tilog.Error(cctx, "failed to create nextgen cluster",
			zap.Uint64("cluster_id", request.GetCluster().GetClusterId()), zap.Error(err))
		return err
	}

	tilog.Info(cctx, "create nextgen cluster action end")
	return nil
}
