package resourcepool

import (
	"context"

	"github.com/tidbcloud/pkgv2/db"
	"go.opentelemetry.io/otel"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/billing"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/eventbus"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/remoteencryptor"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	brrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/br"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster"
	exportrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/export"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/password"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/rcuquota"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	regionservice "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/region"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/idgenerator"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

var tracer = otel.Tracer("domain.resourcepool")

type Service interface {
	PlanResourcePoolCidr(
		ctx context.Context,
		request *globalngv1.PlanResourcePoolCidrRequest,
	) (*globalngv1.PlanResourcePoolCidrResponse, bizerr.BizError)
	ValidateResourcePoolCidr(
		ctx context.Context,
		request *globalngv1.ValidateResourcePoolCidrRequest,
	) (*globalngv1.ValidateResourcePoolCidrResponse, bizerr.BizError)
	CreateResourcePool(ctx context.Context,
		request *globalngv1.CreateResourcePoolRequest) (*entity.ResourcePoolProfile, bizerr.BizError)
	ListResourcePools(ctx context.Context,
		request *globalngv1.ListResourcePoolsRequest) (*globalngv1.ListResourcePoolsResponse, bizerr.BizError)
	UpdateResourcePoolName(ctx context.Context,
		request *globalngv1.UpdateResourcePoolNameRequest) (*entity.ResourcePool, bizerr.BizError)
	UpdateResourcePoolInstanceLimit(ctx context.Context,
		request *globalngv1.UpdateResourcePoolInstanceLimitRequest) (*entity.ResourcePool, bizerr.BizError)
	GetResourcePool(ctx context.Context,
		request *globalngv1.GetResourcePoolRequest) (*entity.ResourcePoolProfile, bizerr.BizError)
	DeleteResourcePool(ctx context.Context,
		request *globalngv1.DeleteResourcePoolRequest) (*entity.ResourcePool, bizerr.BizError)
	GetResourcePoolCapacityConstraints(
		ctx context.Context,
		request *globalngv1.GetResourcePoolCapacityConstraintsRequest,
	) (*globalngv1.ResourcePoolCapacityConstraints, bizerr.BizError)
	ResizeResourcePoolCapacity(
		ctx context.Context,
		request *globalngv1.ResizeResourcePoolCapacityRequest,
	) (*globalngv1.ResizeResourcePoolCapacityResponse, bizerr.BizError)
	UpsertResourcePoolCloudResourceTags(
		ctx context.Context,
		request *globalngv1.UpsertResourcePoolCloudResourceTagsRequest,
	) (*globalngv1.UpsertResourcePoolCloudResourceTagsResponse, bizerr.BizError)
	PauseResourcePool(
		ctx context.Context,
		request *globalngv1.PauseResourcePoolRequest,
	) (*globalngv1.PauseResourcePoolResponse, bizerr.BizError)
	BeginPauseResourcePoolAdmission(
		ctx context.Context,
		request *globalngv1.BeginPauseResourcePoolAdmissionRequest,
	) (*globalngv1.BeginPauseResourcePoolAdmissionResponse, bizerr.BizError)
	ResolvePauseResourcePoolAdmission(
		ctx context.Context,
		request *globalngv1.ResolvePauseResourcePoolAdmissionRequest,
	) (*globalngv1.ResolvePauseResourcePoolAdmissionResponse, bizerr.BizError)
	ResumeResourcePool(
		ctx context.Context,
		request *globalngv1.ResumeResourcePoolRequest,
	) (*globalngv1.ResumeResourcePoolResponse, bizerr.BizError)
	GetResourcePoolPauseResumeOperation(
		ctx context.Context,
		request *globalngv1.GetResourcePoolPauseResumeOperationRequest,
	) (*globalngv1.ResourcePoolPauseResumeOperation, bizerr.BizError)
	ValidatePausePreparation(
		ctx context.Context,
		resourcePool *entity.ResourcePool,
	) ([]*globalngv1.ResourcePoolPauseValidationResult, bizerr.BizError)
	ExpirePauseAdmissions(ctx context.Context) bizerr.BizError
	BackfillResourcePoolMetadata(
		ctx context.Context,
		request MetadataBackfillRequest,
	) (*MetadataBackfillReport, bizerr.BizError)

	GetOrCreateDefaultResourcePool(ctx context.Context,
		pool entity.ResourcePool, requiredRcu uint32) (*entity.ResourcePool, bizerr.BizError)
	FindDefaultResourcePool(ctx context.Context,
		pool entity.ResourcePool) (*entity.ResourcePool, bizerr.BizError)
	CreateTopologyDrivenResourcePool(ctx context.Context,
		pool entity.ResourcePool, poolName string, fixedTopology *globalngv1.FixedTopology) (*entity.ResourcePool, bizerr.BizError)
	PickSharedResourcePool(ctx context.Context,
		pool entity.ResourcePool, orgId uint64) (*entity.ResourcePool, bizerr.BizError)
	UpdateOrgDefaultResourcePool(ctx context.Context,
		request *globalngv1.UpdateOrgDefaultResourcePoolRequest) (*entity.ResourcePoolStrategy, bizerr.BizError)
	ListOwnerResourcePools(ctx context.Context,
		request *globalngv1.ListOwnerResourcePoolsRequest) ([]*entity.ResourcePoolProfile, []*entity.ResourcePoolStrategy, bizerr.BizError)
}

type serviceImpl struct {
	cfg                        *config.Config
	transaction                db.Transactioner
	workflow                   workflow.Workflow
	resourcePoolRepo           resourcepool.Repo
	pauseOperationRepo         resourcepool.PauseOperationRepo
	resourcePoolStrategyRepo   resourcepool.StrategyRepo
	regionalCli                regionalserver.Client
	billingCli                 billing.Client
	regionRepo                 region.Repo
	regionProviderIdentityRepo region.ProviderIdentityRepo
	rcuQuotaRepo               rcuquota.Repo
	regionService              regionservice.Service
	clusterRepo                cluster.Repo
	restoreRepo                brrepo.RestoreRepo
	exportRepo                 exportrepo.ExportRepo
	passwordRepo               password.Repo
	idGeneratorAdapter         idgenerator.IDGenerator
	encryptor                  remoteencryptor.Client
	eventCommitter             eventbus.WALCommitter
}

func NewService(
	cfg *config.Config,
	transaction db.Transactioner,
	flow workflow.Workflow,
	resourcePoolRepo resourcepool.Repo,
	pauseOperationRepo resourcepool.PauseOperationRepo,
	resourcePoolStrategyRepo resourcepool.StrategyRepo,
	regionalCli regionalserver.Client,
	billingCli billing.Client,
	regionRepo region.Repo,
	regionProviderIdentityRepo region.ProviderIdentityRepo,
	rcuQuotaRepo rcuquota.Repo,
	regionService regionservice.Service,
	idGeneratorAdapter idgenerator.IDGenerator,
	clusterRepo cluster.Repo,
	restoreRepo brrepo.RestoreRepo,
	exportRepo exportrepo.ExportRepo,
	passwordRepo password.Repo,
	encryptor remoteencryptor.Client,
	eventCommitter eventbus.WALCommitter,
) Service {
	return &serviceImpl{
		cfg:                        cfg,
		transaction:                transaction,
		workflow:                   flow,
		resourcePoolRepo:           resourcePoolRepo,
		pauseOperationRepo:         pauseOperationRepo,
		resourcePoolStrategyRepo:   resourcePoolStrategyRepo,
		regionalCli:                regionalCli,
		billingCli:                 billingCli,
		regionRepo:                 regionRepo,
		regionProviderIdentityRepo: regionProviderIdentityRepo,
		rcuQuotaRepo:               rcuQuotaRepo,
		regionService:              regionService,
		idGeneratorAdapter:         idGeneratorAdapter,
		clusterRepo:                clusterRepo,
		restoreRepo:                restoreRepo,
		exportRepo:                 exportRepo,
		passwordRepo:               passwordRepo,
		encryptor:                  encryptor,
		eventCommitter:             eventCommitter,
	}
}

func (s *serviceImpl) serverEnv() string {
	if s == nil || s.cfg == nil {
		return ""
	}
	return s.cfg.ServerConfig.Env
}
