package action

import (
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/tioperator"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/encoding/protojson"
)

const DeletePlacementPolicyActionName = "delete-placement-policy"

type DeletePlacementPolicyAction struct {
	dataplaneManager dataplane.Manager
}

func NewDeletePlacementPolicyAction(dataplaneManager dataplane.Manager) actions.Action {
	return &DeletePlacementPolicyAction{
		dataplaneManager: dataplaneManager,
	}
}

func (a *DeletePlacementPolicyAction) Name() string {
	return DeletePlacementPolicyActionName
}

func (a *DeletePlacementPolicyAction) Invoke(args actions.ActionArgs) error {
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "delete placement policy action invoked")

	tidbClusterID, err := args.GetString(workflow.TiDBClusterIdKey)
	if err != nil {
		tilog.Error(ctx, "failed to get tidb cluster id", zap.Error(err))
		return fmt.Errorf("failed to get tidb cluster id: %w", err)
	}

	sharedPoolStr, err := args.GetString(workflow.SharedPoolKey)
	if err != nil {
		tilog.Error(ctx, "failed to get sharedPool", zap.Error(err))
		return fmt.Errorf("failed to get sharedPool: %w", err)
	}

	var sharedPool entity.SharedPool
	if err := protojson.Unmarshal([]byte(sharedPoolStr), &sharedPool); err != nil {
		tilog.Error(ctx, "failed to unmarshal sharedPool", zap.Error(err))
		return fmt.Errorf("failed to unmarshal sharedPool: %w", err)
	}

	remoteCli, err := a.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
	if err != nil {
		tilog.Error(ctx, "failed to get remote client for shared pool", zap.String("dataplaneName", sharedPool.GetDataplaneName()), zap.Error(err))
		return fmt.Errorf("failed to get remote client for shared pool: %w", err)
	}

	if err := tioperator.DeletePlacementPolicy(ctx, remoteCli, sharedPool.GetId(), tidbClusterID); err != nil {
		if status.Code(err) == codes.NotFound {
			tilog.Info(ctx, "placement policy not found, skip delete", zap.String("sharedPoolId", sharedPool.GetId()), zap.String("tidbClusterId", tidbClusterID))
			return nil
		}
		tilog.Error(ctx, "failed to delete placement policy", zap.String("sharedPoolId", sharedPool.GetId()), zap.String("tidbClusterId", tidbClusterID), zap.Error(err))
		return fmt.Errorf("failed to delete placement policy: %w", err)
	}

	tilog.Info(ctx, "delete placement policy successfully", zap.String("sharedPoolId", sharedPool.GetId()), zap.String("tidbClusterId", tidbClusterID))
	return nil
}
