package encryptionkey

import (
	"context"

	"github.com/tidbcloud/pkgv2/db"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	clusterrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster"
	bindingrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/clusterencryptionbinding"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/encryptionkey"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region"
	regionsvc "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/region"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/idgenerator"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

type Service interface {
	GetCmekAccessIamPrincipal(ctx context.Context,
		request *globalngv1.GetCmekAccessIamPrincipalRequest,
	) (*globalngv1.CmekAccessIamPrincipal, bizerr.BizError)
	VerifyCmekAccessIamPrincipal(ctx context.Context,
		request *globalngv1.VerifyCmekAccessIamPrincipalRequest,
	) (*globalngv1.VerifyCmekAccessIamPrincipalResponse, bizerr.BizError)
	GetCustomerManagedEncryptionKey(ctx context.Context,
		request *globalngv1.GetCustomerManagedEncryptionKeyRequest,
	) (*globalngv1.CustomerManagedEncryptionKey, bizerr.BizError)
	CreateCustomerManagedEncryptionKey(ctx context.Context,
		cmek *globalngv1.CustomerManagedEncryptionKey,
		providerIdentityId uint64,
		sharedPoolId string,
		dataplaneId string,
	) (*globalngv1.CustomerManagedEncryptionKey, bizerr.BizError)
	ValidateCustomerManagedEncryptionKey(ctx context.Context,
		cmek *globalngv1.CustomerManagedEncryptionKey,
		sharedPoolId string,
	) bizerr.BizError
	CreateDefaultKeyOrGrantPermission(ctx context.Context,
		organizationId uint64,
		providerIdentityId uint64,
		dataplaneId string,
	) (*entity.EncryptionKey, bizerr.BizError)

	ListEncryptionByIds(ctx context.Context,
		encryptionIds []uint64,
	) ([]*entity.EncryptionKey, bizerr.BizError)
	CvtEncryptionKeyToCmek(
		ctx context.Context,
		key *entity.EncryptionKey,
		providerIdentity *entity.RegionProviderIdentity,
	) (*globalngv1.CustomerManagedEncryptionKey, bizerr.BizError)

	SwitchClusterEncryptionKey(ctx context.Context, req *SwitchEncryptionRequest) bizerr.BizError
	GetEncryptionBinding(ctx context.Context, clusterId uint64) (*entity.ClusterEncryptionBinding, bizerr.BizError)
	BuildCurrentEncryption(ctx context.Context, encryptionKeyId uint64) (*globalngv1.Cluster_DualLayerDataEncryption, bizerr.BizError)

	GetCustomerManagedEncryptionKeyByClusterId(ctx context.Context,
		request *globalngv1.GetCustomerManagedEncryptionKeyByClusterIdRequest,
	) (*globalngv1.CustomerManagedEncryptionKey, bizerr.BizError)
	ListCustomerManagedEncryptionKeys(ctx context.Context,
		request *globalngv1.ListCustomerManagedEncryptionKeysRequest,
	) (*globalngv1.ListCustomerManagedEncryptionKeysResponse, bizerr.BizError)
}

type service struct {
	regionRepo                 region.Repo
	regionProviderIdentityRepo region.ProviderIdentityRepo
	regionService              regionsvc.Service
	encryptionKeyRepo          encryptionkey.Repo
	clusterRepo                clusterrepo.Repo
	bindingRepo                bindingrepo.Repo
	transaction                db.Transactioner
	nextgenCli                 regionalserver.Client
	idGeneratorAdapter         idgenerator.IDGenerator
	workflow                   workflow.Workflow
	resourcePoolResolver       DefaultResourcePoolResolver
}

// DefaultResourcePoolResolver resolves or creates the shared pool used for
// standalone CMEK verification before a TiDB exists.
type DefaultResourcePoolResolver interface {
	GetOrCreateDefaultResourcePool(ctx context.Context, pool entity.ResourcePool, requiredRcu uint32) (
		*entity.ResourcePool,
		bizerr.BizError,
	)
}

func NewService(regionRepo region.Repo,
	regionProviderIdentityRepo region.ProviderIdentityRepo,
	regionService regionsvc.Service,
	encryptionKeyRepo encryptionkey.Repo,
	clusterRepo clusterrepo.Repo,
	bindingRepo bindingrepo.Repo,
	transaction db.Transactioner,
	nextgenCli regionalserver.Client,
	flow workflow.Workflow,
	resourcePoolResolver DefaultResourcePoolResolver,
	idGeneratorAdapter idgenerator.IDGenerator) Service {
	return &service{
		regionRepo:                 regionRepo,
		regionProviderIdentityRepo: regionProviderIdentityRepo,
		regionService:              regionService,
		encryptionKeyRepo:          encryptionKeyRepo,
		clusterRepo:                clusterRepo,
		bindingRepo:                bindingRepo,
		transaction:                transaction,
		nextgenCli:                 nextgenCli,
		idGeneratorAdapter:         idGeneratorAdapter,
		workflow:                   flow,
		resourcePoolResolver:       resourcePoolResolver,
	}
}
