package cluster

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/tidb-mgmt-service/domain/adapter/account"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/eventbus"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/nextgenglobal"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/remoteencryptor"
	"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/tidb-mgmt-service/pkg/common"
	workflowimpl "github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

var tracer = otel.Tracer("Domain.NextGen.Cluster")

//go:generate go run go.uber.org/mock/mockgen@latest -source=$GOFILE -package=mock_$GOPACKAGE -destination=./mock_$GOPACKAGE/mock_$GOFILE
type Service interface {
	CreateCluster(ctx context.Context, cluster *entity.Cluster, validateOnly bool) (*entity.Cluster, error)
	CleanClusters(ctx context.Context, organizationID uint64, needFinalBackup bool) error
	DeleteCluster(
		ctx context.Context,
		clusterId uint64,
		validateOnly bool,
	) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	UpdateCluster(
		ctx context.Context,
		newCluster *entity.Cluster,
		validateOnly bool,
	) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	ListClusters(
		ctx context.Context,
		filter *repository.ClusterFilter,
		page *common.Page,
	) ([]*entity.Cluster, error)
	GetCluster(
		ctx context.Context,
		clusterId uint64,
		view entity.ClusterView,
		includeDeleted bool,
	) (*entity.Cluster, error)
	CountClusters(ctx context.Context, filter *repository.ClusterFilter) (int64, error)
	SetRootPassword(
		ctx context.Context,
		clusterId uint64,
		rootPassword string,
	) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	GetProviderRole(
		ctx context.Context,
		clusterId uint64,
	) (*globalngv1.ProviderRole, error)
	RestoreCluster(
		ctx context.Context,
		restoreClusterEntity *entity.RestoreCluster,
	) error
	RestoreStatus(
		ctx context.Context,
		clusterId string,
	) (*entity.RestoreClusterStatus, error)
	UpdateClusterRuVersion(ctx context.Context, clusterId uint64, ruVersion int32) error
	// MoveCluster moves a single NextGen-backed cluster to a different project (synchronous).
	// displayName is optional: if non-empty it becomes the cluster's new display name after the
	// move; if empty the existing display name is kept unchanged.
	MoveCluster(ctx context.Context, orgId, clusterId, targetProjectId uint64, displayName string) (*entity.Cluster, error)
	// BatchMoveClusters moves multiple NextGen-backed clusters to a different project concurrently.
	// Failures are collected and returned; they do not prevent other clusters from being processed.
	BatchMoveClusters(ctx context.Context,
		orgId, sourceProjectId, targetProjectId uint64,
		servicePlans []string,
	) (total int32, successIds, failedIds []string, err error)
	BindTags(ctx context.Context, organizationID, clusterID uint64, canonicalTags []string) ([]entity.Tag, error)
	UnbindTags(ctx context.Context, organizationID, clusterID uint64, canonicalTags []string) ([]entity.Tag, error)
}

type service struct {
	clusterRepo      repository.ClusterRepository
	resourceTagRepo  repository.ResourceTagRepository
	bindingRepo      repository.ClusterTagBindingRepository
	nextGenCli       nextgenglobal.NextGenGlobal
	customStatusRepo repository.CustomStatusRepository
	accountCli       account.Client
	transaction      db.TransProvider
	encryptor        remoteencryptor.Client
	workflow         workflowimpl.Workflow
	orgRuConfigRepo  repository.OrganizationRuConfigRepository
	eventCommitter   eventbus.WALCommitter
}

func NewService(
	clusterRepo repository.ClusterRepository,
	resourceTagRepo repository.ResourceTagRepository,
	bindingRepo repository.ClusterTagBindingRepository,
	customStatusRepo repository.CustomStatusRepository,
	nextGenCli nextgenglobal.NextGenGlobal,
	accountCli account.Client,
	transaction db.TransProvider,
	encryptor remoteencryptor.Client,
	workflow workflowimpl.Workflow,
	orgRuConfigRepo repository.OrganizationRuConfigRepository,
	eventCommitter eventbus.WALCommitter,
) (Service, error) {
	svc := &service{
		clusterRepo:      clusterRepo,
		resourceTagRepo:  resourceTagRepo,
		bindingRepo:      bindingRepo,
		customStatusRepo: customStatusRepo,
		nextGenCli:       nextGenCli,
		accountCli:       accountCli,
		transaction:      transaction,
		encryptor:        encryptor,
		workflow:         workflow,
		orgRuConfigRepo:  orgRuConfigRepo,
		eventCommitter:   eventCommitter,
	}

	return svc, nil
}
