package action

import (
	"context"
	"strconv"

	"github.com/tidbcloud/fastflow/pkg/entity/run"
	regionaldedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	regionrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/region"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/workflow/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

const (
	DeleteVpcPeering = "delete_vpc_peering"
)

type DeleteVpcPeeringAction struct {
	regionRepo      regionrepo.Repo
	regionServerCli regionserver.Client
}

func NewDeleteVpcPeeringAction(
	regionRepo regionrepo.Repo,
	regionServerCli regionserver.Client,
) *DeleteVpcPeeringAction {
	return &DeleteVpcPeeringAction{
		regionRepo:      regionRepo,
		regionServerCli: regionServerCli,
	}
}

func (c *DeleteVpcPeeringAction) Name() string {
	return DeleteVpcPeering
}

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

	keyVpcPeeringId, ok := ctx.GetVar(common.KeyVpcPeeringId)
	if !ok {
		tilog.Error(cctx, "vpc_peering_id parameter not found")
		return bizerr.New(bizerr.ErrCodeParams, "vpc_peering_id parameter not found")
	}
	if keyVpcPeeringId == "" {
		tilog.Error(cctx, "keyVpcPeeringId parameter is empty")
		return bizerr.New(bizerr.ErrCodeParams, "keyVpcPeeringId parameter is empty")
	}

	vpcPeeringId, err := strconv.ParseUint(keyVpcPeeringId, 10, 64)
	if err != nil {
		tilog.Error(cctx, "failed to parse vpc peering id", zap.Error(err))
		return err
	}

	keyRegionName, ok := ctx.GetVar(common.KeyRegionLogicalName)
	if !ok {
		tilog.Error(cctx, "region_name parameter not found")
		return bizerr.New(bizerr.ErrCodeParams, "cloud_provider_name parameter not found")
	}

	tilog.Info(cctx, "start to delete vpc peering", zap.String("vpc_peering_id", keyVpcPeeringId))
	defer func() {
		tilog.Info(cctx, "end to delete vpc peering", zap.String("vpc_peering_id", keyVpcPeeringId))
		span.End()
	}()

	regionEntity, err := c.regionRepo.GetRegionByLogicalName(cctx, keyRegionName)
	if err != nil {
		tilog.Error(cctx, "find region by id failed", zap.Error(err), zap.Uint64("vpc_peering_id", vpcPeeringId))
		return err
	}

	if err := c.delete(cctx, vpcPeeringId, regionEntity); err != nil {
		tilog.Error(cctx, "failed to delete vpc peering", zap.Error(err), zap.Uint64("vpc_peering_id", vpcPeeringId))
		return err
	}

	return nil
}

func (c *DeleteVpcPeeringAction) delete(ctx context.Context, vpcPeeringId uint64, regionEntity *entity.Region) error {
	err := c.regionServerCli.DeleteVpcPeering(
		ctx,
		regionEntity,
		&regionaldedicatedv1.DeleteVpcPeeringRequest{
			VpcPeeringId: strconv.FormatUint(vpcPeeringId, 10),
		},
	)
	if err != nil {
		if err.ErrCode() == bizerr.ErrCodeRecordNotFound {
			tilog.Info(
				ctx,
				"vpc peering not found",
				zap.Uint64("vpc_peering_id", vpcPeeringId),
				zap.String("region_id", regionEntity.Id.String()),
			)
			return nil
		}
		tilog.Error(
			ctx,
			"failed to delete vpc peering",
			zap.String("region_id", regionEntity.Id.String()),
			zap.Uint64("vpc_peering_id", vpcPeeringId),
			zap.Error(err),
		)
		return err
	}

	return nil
}
