package region

import (
	"cmp"
	"context"
	"errors"
	"math"
	"slices"
	"strconv"
	"strings"

	clusterngv1beta "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	ffentity "github.com/tidbcloud/fastflow/pkg/entity"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	clusterrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster"
	regionrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	workflowcommon "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/workflow/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	utilworkflow "github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

type byocVcpuPoolGroup struct {
	key                 string
	providerName        globalcommonv1.CloudProvider_Name
	regionName          globalcommonv1.Region_Name
	regionDBID          uint64
	region              *globalcommonv1.Region
	totalVcpusLimit     int64
	pools               []*globalngv1.ListByocVcpuPoolsResponse_Pool
	poolIDs             []uint64
	dataplaneIDToPools  map[string][]uint64
	emptyDataplanePools []uint64
}

const byocVcpuPoolUsageConcurrencyLimit = 8

func (s *service) ListByocVcpuPools(
	ctx context.Context,
	req *globalngv1.ListByocVcpuPoolsRequest,
) (*globalngv1.ListByocVcpuPoolsResponse, bizerr.BizError) {
	if req.OrganizationId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "organization_id is required")
	}
	if s.resourcePoolRepo == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "resource pool repo is not configured")
	}
	if s.resourcePoolStrategyRepo == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "resource pool strategy repo is not configured")
	}
	if s.regionRepo == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "region repo is not configured")
	}
	if s.nextgenCli == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "regional client is not configured")
	}

	rpFilter := &resourcepoolrepo.Filter{
		OwnerType:   OwnerTypeOrganization,
		OwnerIds:    []uint64{req.OrganizationId},
		ShowDeleted: false,
		ServicePlans: []entity.ServicePlan{
			globalcommonv1.ServicePlan_BYOC,
		},
	}
	if req.CloudProviderId != globalcommonv1.CloudProvider_NAME_UNSPECIFIED {
		rpFilter.CloudProviderNames = []entity.CloudProviderName{req.CloudProviderId}
	}
	if req.RegionId != globalcommonv1.Region_NAME_UNSPECIFIED {
		rpFilter.RegionNames = []entity.RegionName{req.RegionId}
	}

	resourcePools, _, err := s.resourcePoolRepo.Query(ctx, rpFilter)
	if err != nil {
		return nil, bizerr.WrapError(err, "failed to query resource pools")
	}
	if len(resourcePools) == 0 {
		return &globalngv1.ListByocVcpuPoolsResponse{
			Groups: []*globalngv1.ListByocVcpuPoolsResponse_Group{},
		}, nil
	}

	regionDBIDs := make(map[uint64]struct{}, len(resourcePools))
	groupMap := make(map[string]*byocVcpuPoolGroup, len(resourcePools))
	groupOrder := make([]string, 0, len(resourcePools))

	for _, rp := range resourcePools {
		regionDBIDs[rp.RegionId] = struct{}{}
		key := buildByocVcpuPoolGroupKey(rp.GetProviderId(), rp.GetRegionId())
		group, ok := groupMap[key]
		if !ok {
			group = &byocVcpuPoolGroup{
				key:                 key,
				providerName:        rp.GetProviderId(),
				regionName:          rp.GetRegionId(),
				regionDBID:          rp.RegionId,
				pools:               []*globalngv1.ListByocVcpuPoolsResponse_Pool{},
				poolIDs:             []uint64{},
				dataplaneIDToPools:  map[string][]uint64{},
				emptyDataplanePools: []uint64{},
			}
			groupMap[key] = group
			groupOrder = append(groupOrder, key)
		}

		if rp.GetVcpusLimit() > math.MaxInt64 {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "resource pool %d vcpus_limit exceeds int64", rp.GetId())
		}

		poolLimit := int64(rp.GetVcpusLimit())
		if group.totalVcpusLimit > math.MaxInt64-poolLimit {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "total_vcpus_limit overflow for group %s", key)
		}
		group.totalVcpusLimit += poolLimit

		poolName := strings.TrimSpace(rp.GetName())
		dataplaneID := strings.TrimSpace(rp.GetDataplaneId())

		group.poolIDs = append(group.poolIDs, rp.GetId())
		if dataplaneID == "" {
			group.emptyDataplanePools = append(group.emptyDataplanePools, rp.GetId())
		} else {
			group.dataplaneIDToPools[dataplaneID] = append(group.dataplaneIDToPools[dataplaneID], rp.GetId())
		}

		group.pools = append(group.pools, &globalngv1.ListByocVcpuPoolsResponse_Pool{
			ResourcePoolId: rp.GetId(),
			Name:           poolName,
			DataplaneId:    dataplaneID,
			VcpusLimit:     poolLimit,
		})
	}

	regions, err := s.regionRepo.ListRegions(ctx, regionrepo.Filter{
		DBRegionIds: mapKeysToSlice(regionDBIDs),
		TenantId:    req.OrganizationId,
		ServicePlan: globalcommonv1.ServicePlan_BYOC,
		State:       globalcommonv1.Region_ACTIVE.String(),
		ShowDeleted: false,
	})
	if err != nil {
		return nil, bizerr.WrapError(err, "failed to query regions")
	}

	regionByDBID := make(map[uint64]*entity.Region, len(regions))
	for _, region := range regions {
		regionByDBID[region.RegionID] = region
	}

	strategyFilter := &resourcepoolrepo.StrategyFilter{
		OrganizationIds: []uint64{req.OrganizationId},
		ServicePlans:    []entity.ServicePlan{globalcommonv1.ServicePlan_BYOC},
	}
	if req.CloudProviderId != globalcommonv1.CloudProvider_NAME_UNSPECIFIED {
		strategyFilter.CloudProviderNames = []entity.CloudProviderName{req.CloudProviderId}
	}
	if req.RegionId != globalcommonv1.Region_NAME_UNSPECIFIED {
		strategyFilter.RegionNames = []entity.RegionName{req.RegionId}
	}

	strategies, err := s.resourcePoolStrategyRepo.Query(ctx, strategyFilter)
	if err != nil {
		return nil, bizerr.WrapError(err, "failed to query resource pool strategies")
	}

	strategyByKey := make(map[string]*entity.ResourcePoolStrategy, len(strategies))
	for _, strategy := range strategies {
		key := buildByocVcpuPoolStrategyKey(
			strategy.GetProviderId(),
			strategy.GetRegionId(),
			strategy.GetServicePlan(),
		)
		strategyByKey[key] = strategy
	}

	availableGroups := make([]*byocVcpuPoolGroup, 0, len(groupOrder))
	poolIDs := make([]uint64, 0, len(resourcePools))
	for _, key := range groupOrder {
		group := groupMap[key]
		regionEntity, ok := regionByDBID[group.regionDBID]
		if !ok {
			return nil, bizerr.New(
				bizerr.ErrCodeUnknown,
				"region source-of-truth missing for provider=%s region=%s",
				group.providerName.String(),
				group.regionName.String(),
			)
		}
		group.region = regionEntity.Region
		if err := s.filterAvailableByocVcpuPools(ctx, group, regionEntity); err != nil {
			return nil, err
		}
		if len(group.pools) == 0 {
			continue
		}
		availableGroups = append(availableGroups, group)
		poolIDs = append(poolIDs, group.poolIDs...)
	}

	tidbInstancesByPoolID, bizErr := s.listByocVcpuPoolTidbInstances(ctx, req.GetOrganizationId(), poolIDs)
	if bizErr != nil {
		return nil, bizErr
	}

	groups := make([]*globalngv1.ListByocVcpuPoolsResponse_Group, 0, len(availableGroups))
	for _, group := range availableGroups {
		regionEntity := regionByDBID[group.regionDBID]
		for _, pool := range group.pools {
			pool.TidbInstances = tidbInstancesByPoolID[pool.GetResourcePoolId()]
		}
		if err := s.fillByocVcpuPoolWorkflowState(ctx, group); err != nil {
			return nil, err
		}
		if err := s.fillByocVcpuPoolRuntimeInfo(ctx, group, regionEntity); err != nil {
			return nil, err
		}

		applyDefaultResourcePoolStrategy(ctx, group, strategyByKey)
		logByocVcpuPoolSoftAnomalies(ctx, group, strategyByKey)

		slices.SortFunc(group.pools, func(a, b *globalngv1.ListByocVcpuPoolsResponse_Pool) int {
			if a.GetIsDefault() != b.GetIsDefault() {
				if a.GetIsDefault() {
					return -1
				}
				return 1
			}
			if c := cmp.Compare(a.GetName(), b.GetName()); c != 0 {
				return c
			}
			return cmp.Compare(a.GetResourcePoolId(), b.GetResourcePoolId())
		})

		groups = append(groups, &globalngv1.ListByocVcpuPoolsResponse_Group{
			Region:          group.region,
			TotalVcpusLimit: group.totalVcpusLimit,
			Pools:           group.pools,
		})
	}

	slices.SortFunc(groups, func(a, b *globalngv1.ListByocVcpuPoolsResponse_Group) int {
		if c := cmp.Compare(a.GetRegion().GetCloudProviderId().String(), b.GetRegion().GetCloudProviderId().String()); c != 0 {
			return c
		}
		return cmp.Compare(a.GetRegion().GetId().String(), b.GetRegion().GetId().String())
	})

	return &globalngv1.ListByocVcpuPoolsResponse{
		Groups: groups,
	}, nil
}

func (s *service) filterAvailableByocVcpuPools(
	ctx context.Context,
	group *byocVcpuPoolGroup,
	regionEntity *entity.Region,
) bizerr.BizError {
	sharedPoolIDs := make([]string, 0, len(group.pools))
	for _, pool := range group.pools {
		sharedPoolIDs = append(sharedPoolIDs, strconv.FormatUint(pool.GetResourcePoolId(), 10))
	}

	sharedPoolsResp, err := s.nextgenCli.ListSharedPools(ctx, regionEntity, &clusterngv1beta.ListSharedPoolsRequest{
		Ids: sharedPoolIDs,
	})
	if err != nil {
		return bizerr.WrapError(err, "failed to list shared pools for byoc vcpu pool group %s", group.key)
	}
	if sharedPoolsResp == nil {
		return bizerr.New(bizerr.ErrCodeUnknown, "received nil response for shared pools for byoc vcpu pool group %s", group.key)
	}

	sharedPoolByID := make(map[string]*clusterngv1beta.SharedPool, len(sharedPoolsResp.GetSharedPools()))
	for _, sharedPool := range sharedPoolsResp.GetSharedPools() {
		sharedPoolByID[sharedPool.GetId()] = sharedPool
	}

	availablePools := make([]*globalngv1.ListByocVcpuPoolsResponse_Pool, 0, len(group.pools))
	totalVcpusLimit := int64(0)
	poolIDs := make([]uint64, 0, len(group.pools))
	dataplaneIDToPools := make(map[string][]uint64, len(group.dataplaneIDToPools))
	emptyDataplanePools := make([]uint64, 0, len(group.emptyDataplanePools))

	for _, pool := range group.pools {
		sharedPoolID := strconv.FormatUint(pool.GetResourcePoolId(), 10)
		sharedPool := sharedPoolByID[sharedPoolID]
		if sharedPool == nil {
			tilog.Warn(ctx, "byoc vcpu pool shared pool is missing, skip pool",
				zap.String("group_key", group.key),
				zap.Uint64("resource_pool_id", pool.GetResourcePoolId()))
			continue
		}
		if !shouldIncludeByocVcpuPool(sharedPool) {
			tilog.Warn(ctx, "byoc vcpu pool shared pool is not available, skip pool",
				zap.String("group_key", group.key),
				zap.Uint64("resource_pool_id", pool.GetResourcePoolId()),
				zap.String("phase", sharedPool.GetPhase().String()))
			continue
		}

		backfillByocVcpuPoolZoneConfig(pool, sharedPool.GetProvider().GetNodeZones())
		pool.State = byocVcpuPoolState(sharedPool)
		totalVcpusLimit += pool.GetVcpusLimit()
		poolIDs = append(poolIDs, pool.GetResourcePoolId())
		if pool.GetDataplaneId() == "" {
			emptyDataplanePools = append(emptyDataplanePools, pool.GetResourcePoolId())
		} else {
			dataplaneIDToPools[pool.GetDataplaneId()] = append(dataplaneIDToPools[pool.GetDataplaneId()], pool.GetResourcePoolId())
		}
		availablePools = append(availablePools, pool)
	}

	group.totalVcpusLimit = totalVcpusLimit
	group.pools = availablePools
	group.poolIDs = poolIDs
	group.dataplaneIDToPools = dataplaneIDToPools
	group.emptyDataplanePools = emptyDataplanePools
	return nil
}

func shouldIncludeByocVcpuPool(
	sharedPool *clusterngv1beta.SharedPool,
) bool {
	switch sharedPool.GetPhase() {
	case clusterngv1beta.SharedPool_AVAILABLE,
		clusterngv1beta.SharedPool_MODIFYING:
		return true
	default:
		return false
	}
}

func byocVcpuPoolState(
	sharedPool *clusterngv1beta.SharedPool,
) globalngv1.ListByocVcpuPoolsResponse_PoolState {
	if sharedPool.GetPhase() == clusterngv1beta.SharedPool_MODIFYING {
		return globalngv1.ListByocVcpuPoolsResponse_MODIFYING
	}
	return globalngv1.ListByocVcpuPoolsResponse_AVAILABLE
}

func (s *service) listByocVcpuPoolTidbInstances(
	ctx context.Context,
	organizationID uint64,
	poolIDs []uint64,
) (map[uint64][]*globalngv1.ListByocVcpuPoolsResponse_TidbInstance, bizerr.BizError) {
	instancesByPoolID := make(map[uint64][]*globalngv1.ListByocVcpuPoolsResponse_TidbInstance, len(poolIDs))
	if len(poolIDs) == 0 {
		return instancesByPoolID, nil
	}
	if s.clusterRepo == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "cluster repo is not configured")
	}
	if s.workflow == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "workflow is not configured")
	}

	poolIDSet := make(map[uint64]struct{}, len(poolIDs))
	for _, poolID := range poolIDs {
		poolIDSet[poolID] = struct{}{}
	}

	liveClusters, err := s.clusterRepo.Query(ctx, &clusterrepo.Filter{
		OrganizationIds: []uint64{organizationID},
		SharedPoolIds:   poolIDs,
		ShowDeleted:     false,
	})
	if err != nil {
		return nil, bizerr.WrapError(err, "failed to query live TiDB instances for BYOC vCPU pools")
	}

	instancesByClusterID := make(map[uint64]*globalngv1.ListByocVcpuPoolsResponse_TidbInstance, len(liveClusters))
	clusterPoolID := make(map[uint64]uint64, len(liveClusters))
	for _, cluster := range liveClusters {
		poolID, parseErr := parseByocVcpuPoolID(cluster)
		if parseErr != nil {
			return nil, parseErr
		}
		if _, ok := poolIDSet[poolID]; !ok {
			return nil, bizerr.New(
				bizerr.ErrCodeUnknown,
				"cluster %d returned outside requested BYOC vCPU pools: %d",
				cluster.GetClusterId(),
				poolID,
			)
		}
		instance := &globalngv1.ListByocVcpuPoolsResponse_TidbInstance{
			ClusterId: cluster.GetClusterId(),
			BaseState: cluster.GetState(),
		}
		instancesByClusterID[cluster.GetClusterId()] = instance
		clusterPoolID[cluster.GetClusterId()] = poolID
	}

	workflowInstances, workflowErr := s.workflow.ListRunningDagInstancesByOrgId(
		ctx,
		[]string{workflowcommon.DeleteCluster},
		strconv.FormatUint(organizationID, 10),
	)
	if workflowErr != nil {
		return nil, bizerr.WrapError(workflowErr, "failed to list running TiDB instance deletion workflows")
	}

	deletingClusterIDs := make(map[uint64]struct{}, len(workflowInstances))
	for _, workflowInstance := range workflowInstances {
		if workflowInstance == nil {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "received nil TiDB instance deletion workflow")
		}
		clusterID, extractErr := deletingClusterID(workflowInstance.Tags)
		if extractErr != nil {
			return nil, extractErr
		}
		deletingClusterIDs[clusterID] = struct{}{}
	}

	missingClusterIDs := make([]uint64, 0, len(deletingClusterIDs))
	for clusterID := range deletingClusterIDs {
		if instance := instancesByClusterID[clusterID]; instance != nil {
			instance.Deleting = true
			continue
		}
		missingClusterIDs = append(missingClusterIDs, clusterID)
	}

	if len(missingClusterIDs) > 0 {
		deletedClusters, queryErr := s.clusterRepo.Query(ctx, &clusterrepo.Filter{
			OrganizationIds: []uint64{organizationID},
			ClusterIds:      missingClusterIDs,
			ShowDeleted:     true,
		})
		if queryErr != nil {
			return nil, bizerr.WrapError(queryErr, "failed to query deleting TiDB instances")
		}

		foundClusterIDs := make(map[uint64]struct{}, len(deletedClusters))
		for _, cluster := range deletedClusters {
			if cluster == nil {
				return nil, bizerr.New(bizerr.ErrCodeUnknown, "received nil deleting TiDB instance")
			}
			foundClusterIDs[cluster.GetClusterId()] = struct{}{}
			if cluster.GetSharedPoolId() == "" {
				continue
			}
			poolID, parseErr := parseByocVcpuPoolID(cluster)
			if parseErr != nil {
				return nil, parseErr
			}
			if _, ok := poolIDSet[poolID]; !ok {
				continue
			}
			instancesByClusterID[cluster.GetClusterId()] = &globalngv1.ListByocVcpuPoolsResponse_TidbInstance{
				ClusterId: cluster.GetClusterId(),
				BaseState: cluster.GetState(),
				Deleting:  true,
			}
			clusterPoolID[cluster.GetClusterId()] = poolID
		}
		for _, clusterID := range missingClusterIDs {
			if _, ok := foundClusterIDs[clusterID]; !ok {
				return nil, bizerr.New(
					bizerr.ErrCodeUnknown,
					"cluster %d referenced by deletion workflow was not found",
					clusterID,
				)
			}
		}
	}

	for clusterID, instance := range instancesByClusterID {
		poolID := clusterPoolID[clusterID]
		instancesByPoolID[poolID] = append(instancesByPoolID[poolID], instance)
	}
	for poolID := range instancesByPoolID {
		slices.SortFunc(instancesByPoolID[poolID], func(a, b *globalngv1.ListByocVcpuPoolsResponse_TidbInstance) int {
			return cmp.Compare(a.GetClusterId(), b.GetClusterId())
		})
	}

	return instancesByPoolID, nil
}

func parseByocVcpuPoolID(cluster *entity.Cluster) (uint64, bizerr.BizError) {
	if cluster == nil {
		return 0, bizerr.New(bizerr.ErrCodeUnknown, "received nil cluster while listing BYOC vCPU pool instances")
	}
	poolID, err := strconv.ParseUint(cluster.GetSharedPoolId(), 10, 64)
	if err != nil || poolID == 0 {
		return 0, bizerr.New(
			bizerr.ErrCodeUnknown,
			"cluster %d has invalid shared pool id %q",
			cluster.GetClusterId(),
			cluster.GetSharedPoolId(),
		)
	}
	return poolID, nil
}

func deletingClusterID(tags []ffentity.DagInstanceTag) (uint64, bizerr.BizError) {
	for _, tag := range tags {
		if tag.Key != utilworkflow.BizIdKey {
			continue
		}
		clusterID, err := strconv.ParseUint(tag.Value, 10, 64)
		if err != nil || clusterID == 0 {
			return 0, bizerr.New(bizerr.ErrCodeUnknown, "deletion workflow has invalid biz-id %q", tag.Value)
		}
		return clusterID, nil
	}
	return 0, bizerr.New(bizerr.ErrCodeUnknown, "deletion workflow is missing biz-id tag")
}

func applyDefaultResourcePoolStrategy(
	ctx context.Context,
	group *byocVcpuPoolGroup,
	strategyByKey map[string]*entity.ResourcePoolStrategy,
) {
	strategyKey := buildByocVcpuPoolStrategyKey(group.providerName, group.regionName, globalcommonv1.ServicePlan_BYOC)
	strategy := strategyByKey[strategyKey]
	if strategy == nil {
		return
	}

	defaultPoolID := strategy.GetDefaultResourcePoolId()
	for _, pool := range group.pools {
		if pool.GetResourcePoolId() == defaultPoolID {
			pool.IsDefault = true
			return
		}
	}

	tilog.Warn(ctx, "default resource pool strategy points to a pool outside active set",
		zap.String("group_key", group.key),
		zap.Uint64("default_resource_pool_id", defaultPoolID),
		zap.Uint64s("active_pool_ids", group.poolIDs))
}

func logByocVcpuPoolSoftAnomalies(
	ctx context.Context,
	group *byocVcpuPoolGroup,
	strategyByKey map[string]*entity.ResourcePoolStrategy,
) {
	strategyKey := buildByocVcpuPoolStrategyKey(group.providerName, group.regionName, globalcommonv1.ServicePlan_BYOC)
	if strategyByKey[strategyKey] == nil {
		tilog.Warn(ctx, "byoc vcpu pool group has no default strategy",
			zap.String("group_key", group.key),
			zap.Uint64s("pool_ids", group.poolIDs))
	}

	if len(group.emptyDataplanePools) > 0 {
		tilog.Warn(ctx, "byoc vcpu pool group has pools with empty dataplane id",
			zap.String("group_key", group.key),
			zap.Uint64s("pool_ids", group.emptyDataplanePools))
	}

	for dataplaneID, poolIDs := range group.dataplaneIDToPools {
		if len(poolIDs) <= 1 {
			continue
		}
		tilog.Warn(ctx, "byoc vcpu pool group has duplicate dataplane ids",
			zap.String("group_key", group.key),
			zap.String("dataplane_id", dataplaneID),
			zap.Uint64s("pool_ids", poolIDs))
	}
}

func buildByocVcpuPoolGroupKey(
	providerName globalcommonv1.CloudProvider_Name,
	regionName globalcommonv1.Region_Name,
) string {
	return providerName.String() + "|" + regionName.String()
}

func buildByocVcpuPoolStrategyKey(
	providerName globalcommonv1.CloudProvider_Name,
	regionName globalcommonv1.Region_Name,
	servicePlan globalcommonv1.ServicePlan,
) string {
	return providerName.String() + "|" + regionName.String() + "|" + servicePlan.String()
}

func mapKeysToSlice(m map[uint64]struct{}) []uint64 {
	keys := make([]uint64, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	return keys
}

func (s *service) fillByocVcpuPoolRuntimeInfo(
	ctx context.Context,
	group *byocVcpuPoolGroup,
	regionEntity *entity.Region,
) bizerr.BizError {
	g, groupCtx := errgroup.WithContext(ctx)
	g.SetLimit(byocVcpuPoolUsageConcurrencyLimit)

	for _, pool := range group.pools {
		g.Go(func() error {
			usageResp, err := s.nextgenCli.GetSharedPoolVcpusUsage(groupCtx, regionEntity, &clusterngv1beta.GetSharedPoolVcpusUsageRequest{
				SharedPoolId: strconv.FormatUint(pool.GetResourcePoolId(), 10),
			})
			if err != nil {
				return bizerr.WrapError(err, "failed to get shared pool vcpus usage for resource pool %d", pool.GetResourcePoolId())
			}
			if usageResp == nil {
				return bizerr.New(
					bizerr.ErrCodeUnknown,
					"received nil response for shared pool vcpus usage for resource pool %d",
					pool.GetResourcePoolId(),
				)
			}
			if usageResp.GetVcpusUsed() > math.MaxInt64 {
				return bizerr.New(bizerr.ErrCodeUnknown, "resource pool %d vcpus_used exceeds int64", pool.GetResourcePoolId())
			}
			pool.VcpusUsed = int64(usageResp.GetVcpusUsed())

			return nil
		})
	}
	if err := g.Wait(); err != nil {
		var bizErr bizerr.BizError
		if errors.As(err, &bizErr) {
			return bizErr
		}
		return bizerr.WrapError(err, "failed to fill byoc vcpu pool usage")
	}
	return nil
}

func (s *service) fillByocVcpuPoolWorkflowState(
	ctx context.Context,
	group *byocVcpuPoolGroup,
) bizerr.BizError {
	if len(group.pools) == 0 {
		return nil
	}
	if s.workflow == nil {
		return bizerr.New(bizerr.ErrCodeUnknown, "workflow is not configured")
	}

	bizIDs := make([]string, 0, len(group.pools))
	for _, pool := range group.pools {
		bizIDs = append(bizIDs, strconv.FormatUint(pool.GetResourcePoolId(), 10))
	}

	instances, err := s.workflow.ListRunningDagInstancesByBizIds(
		ctx,
		[]string{workflowcommon.ScaleByocSharedComponents},
		bizIDs,
	)
	if err != nil {
		return bizerr.WrapError(err, "failed to list running scale workflows for byoc vcpu pool group %s", group.key)
	}

	runningByBizID := byocVcpuPoolRunningWorkflowMap(instances)
	for _, pool := range group.pools {
		bizID := strconv.FormatUint(pool.GetResourcePoolId(), 10)
		if _, ok := runningByBizID[bizID]; ok {
			pool.State = globalngv1.ListByocVcpuPoolsResponse_MODIFYING
			continue
		}
		if pool.GetState() == globalngv1.ListByocVcpuPoolsResponse_POOL_STATE_UNSPECIFIED {
			pool.State = globalngv1.ListByocVcpuPoolsResponse_AVAILABLE
		}
	}

	return nil
}

func backfillByocVcpuPoolZoneConfig(pool *globalngv1.ListByocVcpuPoolsResponse_Pool, nodeZones []string) {
	if pool == nil || len(nodeZones) == 0 {
		return
	}

	pool.ZoneConfig = &globalngv1.ResourcePool_ZoneConfig{
		NodeZones: append([]string(nil), nodeZones...),
	}
	if len(nodeZones) == 1 {
		pool.HighAvailabilityType = globalngv1.ResourcePool_ZONAL
		return
	}
	pool.HighAvailabilityType = globalngv1.ResourcePool_REGIONAL
}
