package tidbgroup

import (
	"context"
	"errors"
	"strconv"

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

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/resourcepoolpolicy"
	servicecommon "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/common"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/workflow/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

func (s *serviceImpl) DeleteTiDBGroup(
	ctx context.Context,
	req *globalngv1.DeleteTiDBGroupRequest,
) (*emptypb.Empty, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "Domain.DeleteTiDBGroup")
	defer span.End()

	if req.ClusterId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster_id is required")
	}
	if req.TidbGroupId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb_group_id is required")
	}

	cluster, err := s.clusterRepo.Get(ctx, req.ClusterId, false)
	if err != nil {
		return nil, err
	}
	resourcePoolID, parseErr := strconv.ParseUint(cluster.GetSharedPoolId(), 10, 64)
	if parseErr != nil || resourcePoolID == 0 {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "cluster has an invalid resource pool id")
	}
	resourcePool, err := s.resourcePoolRepo.Load(ctx, resourcePoolID, false)
	if err != nil {
		return nil, err
	}
	if lifecycleErr := servicecommon.RequireResourcePoolLifecycle(
		resourcePool,
		resourcepoolpolicy.OperationDeleteInstance,
	); lifecycleErr != nil {
		return nil, lifecycleErr
	}

	tidbGroup, err := s.tidbgroupRepo.Load(ctx, req.ClusterId, req.TidbGroupId)
	if err != nil {
		return nil, err
	}

	terr := s.transaction.Transaction(ctx, func(ctx context.Context) error {
		lockedResourcePool, lockErr := s.resourcePoolRepo.LoadForUpdate(ctx, resourcePoolID, false)
		if lockErr != nil {
			return lockErr
		}
		if lifecycleErr := servicecommon.RequireResourcePoolLifecycle(
			lockedResourcePool,
			resourcepoolpolicy.OperationDeleteInstance,
		); lifecycleErr != nil {
			return lifecycleErr
		}
		if err := s.publicConnectionSettingRepo.DeleteByTidbGroupId(ctx, req.TidbGroupId); err != nil {
			return err
		}

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

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

		return nil
	})

	if terr != nil {
		var be bizerr.BizError
		if errors.As(terr, &be) {
			return nil, be
		}
		return nil, bizerr.WrapGormError(terr, "delete tidb group failed")
	}

	return &emptypb.Empty{}, nil
}
