package resourcepool

import (
	"context"
	"time"

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

type Cursor struct {
	CreatedAt time.Time
	ID        uint64
}

type Lifecycle struct {
	State     globalngv1.ResourcePool_LifecycleState
	IsDeleted bool
}

type Filter struct {
	CloudProviderNames    []entity.CloudProviderName
	RegionNames           []entity.RegionName
	ServicePlans          []entity.ServicePlan
	HighAvailabilityTypes []globalngv1.ResourcePool_HighAvailabilityType
	LifecycleStates       []globalngv1.ResourcePool_LifecycleState

	CreatedBefore time.Time
	CreatedAfter  time.Time

	Ids         []uint64
	Name        string
	ShowDeleted bool

	OwnerType int
	OwnerIds  []uint64

	RegionId uint64

	Offset   int64
	PageSize int64
	Cursor   *Cursor
}

type IdleInfo struct {
	ResourcePoolId uint64
	OwnerId        uint64
	CreatedAt      time.Time
	IdleStartTime  time.Time
	LastClusterId  uint64
}

type MetadataBackfillPatch struct {
	ResourcePoolID uint64

	NormalizedName *string
	CIDRStartIPv4  []byte
	CIDREndIPv4    []byte

	LifecycleState    *globalngv1.ResourcePool_LifecycleState
	VcpuLimitMode     *globalngv1.ResourcePool_VcpuLimitMode
	CloudResourceTags map[string]string
	BackfillTags      bool
}

type MetadataBackfillResult struct {
	NormalizedName    bool
	CIDR              bool
	LifecycleState    bool
	VcpuLimitMode     bool
	CloudResourceTags bool
}

//go:generate go run go.uber.org/mock/mockgen@latest -source=$GOFILE -package=mock_$GOPACKAGE -destination=./mock_$GOPACKAGE/mock_$GOFILE
type Repo interface {
	Create(ctx context.Context, resourcePool *entity.ResourcePool) bizerr.BizError
	Save(ctx context.Context, resourcePool *entity.ResourcePool) bizerr.BizError
	Load(ctx context.Context, id uint64, showDeleted bool) (*entity.ResourcePool, bizerr.BizError)
	LoadForUpdate(ctx context.Context, id uint64, showDeleted bool) (*entity.ResourcePool, bizerr.BizError)
	LoadLifecycleForUpdate(ctx context.Context, id uint64, showDeleted bool) (*Lifecycle, bizerr.BizError)
	ListDueAutomaticResume(ctx context.Context) ([]uint64, bizerr.BizError)
	Delete(ctx context.Context, id uint64) bizerr.BizError
	UpdateDataplaneId(ctx context.Context, id uint64, dataplaneId string) bizerr.BizError
	UpdateLifecycle(
		ctx context.Context,
		id uint64,
		state globalngv1.ResourcePool_LifecycleState,
		messageCode string,
	) bizerr.BizError
	CompletePauseResumeLifecycle(
		ctx context.Context,
		id uint64,
		expectedState globalngv1.ResourcePool_LifecycleState,
		targetState globalngv1.ResourcePool_LifecycleState,
	) (bool, bizerr.BizError)
	CompareAndSetLifecycle(
		ctx context.Context,
		id uint64,
		expectedState globalngv1.ResourcePool_LifecycleState,
		expectedMessageCode string,
		expectedUpdatedAt time.Time,
		state globalngv1.ResourcePool_LifecycleState,
		messageCode string,
	) (bool, bizerr.BizError)
	UpdateInstanceLimit(ctx context.Context, id uint64, limit uint64) bizerr.BizError
	UpdateVcpuPolicy(
		ctx context.Context,
		id uint64,
		mode globalngv1.ResourcePool_VcpuLimitMode,
		limit uint64,
	) bizerr.BizError
	UpdateCloudResourceTags(ctx context.Context, id uint64, tags map[string]string) bizerr.BizError
	// LockOrganizationNamespace must be called inside the transaction that performs
	// the corresponding name or CIDR conflict check and mutation.
	LockOrganizationNamespace(ctx context.Context, organizationID uint64) bizerr.BizError
	HasLiveNormalizedName(
		ctx context.Context,
		organizationID uint64,
		normalizedName string,
		excludeResourcePoolID uint64,
	) (bool, bizerr.BizError)
	HasLiveCIDROverlap(
		ctx context.Context,
		organizationID uint64,
		cloudProvider entity.CloudProviderName,
		region entity.RegionName,
		cidrStart []byte,
		cidrEnd []byte,
		excludeResourcePoolID uint64,
	) (bool, bizerr.BizError)
	ListLiveBYOCOrganizationResourcePoolsInRegion(
		ctx context.Context,
		organizationID uint64,
		cloudProvider entity.CloudProviderName,
		region entity.RegionName,
	) ([]*entity.ResourcePool, bizerr.BizError)
	InspectLiveCIDROverlapForBackfill(
		ctx context.Context,
		organizationID uint64,
		cloudProvider entity.CloudProviderName,
		region entity.RegionName,
		cidrStart []byte,
		cidrEnd []byte,
		excludeResourcePoolID uint64,
	) (bool, []uint64, bizerr.BizError)
	BackfillMetadata(
		ctx context.Context,
		patch *MetadataBackfillPatch,
	) (MetadataBackfillResult, bizerr.BizError)

	Query(ctx context.Context, filter *Filter) ([]*entity.ResourcePool, int64, bizerr.BizError)

	ListIdle(ctx context.Context) ([]*IdleInfo, bizerr.BizError)
}

type StrategyFilter struct {
	Ids []uint64

	CloudProviderNames []entity.CloudProviderName
	RegionNames        []entity.RegionName
	ServicePlans       []entity.ServicePlan

	OrganizationIds        []uint64
	DefaultResourcePoolIds []uint64
}

type StrategyRepo interface {
	Create(ctx context.Context, strategy *entity.ResourcePoolStrategy) bizerr.BizError
	Save(ctx context.Context, strategy *entity.ResourcePoolStrategy) bizerr.BizError
	SwitchDefaultResourcePool(ctx context.Context, strategyId uint64, oldPoolId uint64, newPoolId uint64) (bool, bizerr.BizError)
	Load(ctx context.Context, id uint64) (*entity.ResourcePoolStrategy, bizerr.BizError)
	Delete(ctx context.Context, id uint64) bizerr.BizError

	Query(ctx context.Context, filter *StrategyFilter) ([]*entity.ResourcePoolStrategy, bizerr.BizError)
	GetByOrgProviderRegion(ctx context.Context,
		orgId uint64,
		provider entity.CloudProviderName,
		region entity.RegionName,
		servicePlan entity.ServicePlan) (*entity.ResourcePoolStrategy, bizerr.BizError)
}
