package cluster

import (
	"context"
	"fmt"

	"github.com/samber/lo"
	accountsdk "github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

const starterClusterMaxCount = 100

var starterUnlimitedFeatureFlags = []string{
	"instance_capacity_plan_billed_overage",
	"instance_capacity_plan_free_overage",
}

func (svc *service) lockStarterInventory(ctx context.Context, cluster *entity.Cluster) (*entity.Lock, bool, bizerr.BizError) {
	if cluster == nil || cluster.ServicePlan != openapicommonv1beta1.ServicePlan_Starter {
		return nil, false, nil
	}

	starterLock := &entity.Lock{
		Key:         fmt.Sprintf("starter_cluster_inventory_%d", cluster.OrganizationId),
		TimeoutSecs: 29,
	}

	if err := svc.lockRepo.Lock(ctx, starterLock); err != nil {
		tilog.Error(ctx, "starter inventory lock failed", zap.Error(err))
		return nil, false, bizerr.New(bizerr.ErrCodeUnknown, "create cluster failed, please try again")
	}

	return starterLock, true, nil
}

func (svc *service) releaseStarterInventoryLock(ctx context.Context, lock *entity.Lock) {
	if lock == nil {
		return
	}

	rctx := context.WithoutCancel(ctx)
	if err := svc.lockRepo.Release(rctx, lock); err != nil {
		tilog.Error(rctx, "release starter inventory lock failed", zap.Any("lock", lock), zap.Error(err))
		return
	}

	tilog.Debug(rctx, "release starter inventory lock success", zap.Any("lock", lock))
}

func (svc *service) checkStarterInventoryLimit(ctx context.Context, orgID uint64) bizerr.BizError {
	hitFlagNames, err := svc.accountCli.ListHitFeatureFlagNames(ctx, &accountsdk.ListHitFeatureFlagNamesReq{OrgId: orgID})
	if err != nil {
		return bizerr.WrapGrpcError(err, "failed to list hit feature flag names")
	}

	if lo.ContainsBy(hitFlagNames, func(flagName string) bool {
		return lo.Contains(starterUnlimitedFeatureFlags, flagName)
	}) {
		return nil
	}

	count, err := svc.clusterRepo.CountClusterByFilter(
		ctx,
		&repository.ClusterFilter{
			OrganizationIds: []uint64{orgID},
			ServicePlans:    []string{openapicommonv1beta1.ServicePlan_Starter.String()},
			QueryDeleted:    false,
			DeployType:      entity.DeployTypeServerless,
		},
	)
	if err != nil {
		return bizerr.WrapGormError(err, "failed to query starter clusters for org")
	}

	if count >= starterClusterMaxCount {
		tilog.Warn(
			ctx,
			"starter cluster limit reached",
			zap.Uint64("organization_id", orgID),
			zap.Int64("current_count", count),
			zap.Int("limit", starterClusterMaxCount),
		)
		return bizerr.New(
			bizerr.ErrCodeParams,
			"Your organization has reached the instance capacity limit of 100 combined instances and branches. To unlock more capacity, apply for our Instance Capacity Plan. https://www.pingcap.com/programs/agentic-ai-instance-capacity.",
		)
	}

	return nil
}
