package tidbgroup

import (
	"context"
	"strconv"

	"google.golang.org/protobuf/types/known/emptypb"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"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/clusterevent"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

func (s *service) DeleteTidbNodeGroup(
	ctx context.Context,
	req *globaldedicatedv1.DeleteTidbNodeGroupRequest,
) (*emptypb.Empty, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "Domain.deleteTidbGroupSvc")
	defer span.End()

	err := s.transaction.Transaction(ctx, func(ctx context.Context) error {
		tidbGroup, err := s.tidbGroupRepo.Load(ctx, req.GetClusterId(), req.GetId())
		if err != nil {
			return err
		}

		cluster, err := s.clusterRepo.GetAndLoadComponents(ctx, tidbGroup.ClusterId)
		if err != nil {
			return err
		}

		defaultGroup, err := s.tidbGroupRepo.LoadDefault(ctx, tidbGroup.ClusterId)
		if err != nil {
			return err
		}

		if cluster.State != globaldedicatedv1.Cluster_AVAILABLE {
			return bizerr.New(bizerr.ErrCodeParams, "cluster is not available for this action")
		}

		if tidbGroup.Default {
			return bizerr.New(bizerr.ErrCodeParams, "Default TiDB node group can not be deleted")
		}

		if req.GetValidateOnly() {
			return nil
		}

		// before scaling information
		beforeTopology := entity.BuildClusterTopology(cluster, defaultGroup.Id)

		// update MetaDB
		_, err = s.privateLinkEndpointRepo.DeleteByTidbGroupID(ctx, cluster.ClusterId, tidbGroup.Id)
		if err != nil {
			return err
		}

		if err := s.trafficFilterRepo.DeleteByTidbGroupId(ctx, cluster.ClusterId, tidbGroup.Id); err != nil {
			return err
		}

		err = s.tidbGroupRepo.Delete(ctx, tidbGroup)
		if err != nil {
			return err
		}

		err = s.componentRepo.DeleteByTidbGroupId(ctx, tidbGroup.ClusterId, tidbGroup.Id)
		if err != nil {
			return err
		}

		newCluster, err := s.clusterRepo.GetAndLoadComponents(ctx, cluster.ClusterId)
		if err != nil {
			return err
		}
		afterTopology := entity.BuildClusterTopology(newCluster, defaultGroup.Id)
		scaleEvent := entity.NewClusterEventBuilder(ctx, cluster, clusterevent.EventTypeScaling).
			SetTopologyBefore(beforeTopology).
			SetTopologyAfter(afterTopology).
			Build()
		if err := s.eventCommitter.Write(ctx, clusterevent.TopicClusterEvent, scaleEvent); err != nil {
			return err
		}

		// Start async reconciliation workflow.
		workFlowErr := s.workflow.RunDagWithBizId(
			ctx,
			common.DeleteTidbGroup,
			strconv.FormatUint(cluster.OrganizationId, 10),
			strconv.FormatUint(cluster.ProjectId, 10),
			strconv.FormatUint(cluster.ClusterId, 10),
			map[string]string{
				workflow.KeyTraceId:             workflow.GetTraceId(ctx),
				common.KeyClusterId:             strconv.FormatUint(cluster.ClusterId, 10),
				common.KeyTidbGroupIds:          strconv.FormatUint(tidbGroup.Id, 10),
				common.KeyPrivateLinkServiceIds: strconv.FormatUint(tidbGroup.Id, 10),
			},
		)
		if workFlowErr != nil {
			return bizerr.WrapErrorWithCode(workFlowErr, bizerr.ErrCodeUnknown, "create async workflow failed")
		}

		return nil
	})
	if err != nil {
		return nil, bizerr.WrapError(err, "delete work pool failed")
	}

	return &emptypb.Empty{}, nil
}
