package cluster

import (
	"context"

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

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/account"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/dataflow"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/dedicatedglobal"
	"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.Dedicated.Cluster")

type Service interface {
	CreateCluster(
		ctx context.Context,
		cluster *entity.Cluster,
		clusterConfiguration *globaldedicatedv1.ClusterConfiguration,
		validateOnly bool,
	) (*entity.Cluster, error)
	// view: Local early return only; Basic/Full do not differ on Regional enrichment today.
	// globalMetaOnly: explicit opt-in to skip Regional; not derived from view (see get.go).
	GetCluster(
		ctx context.Context,
		clusterId uint64,
		view entity.ClusterView,
		includeDeleted bool,
		globalMetaOnly bool,
	) (*entity.Cluster, error)
	ListClusters(ctx context.Context, filter *repository.ClusterFilter, page *common.Page) ([]*entity.Cluster, error)
	CountClusters(ctx context.Context, filter *repository.ClusterFilter) (int64, error)
	DeleteCluster(
		ctx context.Context,
		clusterId uint64,
		needFinalBackup, validateOnly bool,
	) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	PauseCluster(ctx context.Context, clusterId uint64) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	ResumeCluster(ctx context.Context, clusterId uint64) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	UpdateCluster(
		ctx context.Context,
		newCluster *entity.Cluster,
		clusterComponents []*globaldedicatedv1.Cluster_ClusterComponent,
		forceScale, validateOnly bool,
		sourceOperation string,
		requestedFields []string,
	) (*entity.Cluster, *entity.ClusterCustomStatus, []*globaldedicatedv1.Cluster_ClusterComponent, error)
	SetRootPassword(
		ctx context.Context,
		clusterId uint64,
		rootPassword string,
	) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	GetComponentNodeInfo(ctx context.Context, clusterId uint64) (*globaldedicatedv1.ComponentNodeInfo, error)
	CleanClusters(ctx context.Context, OrganizationId uint64, needFinalBackup bool) error
	ApplyCluster(
		ctx context.Context,
		clusterId uint64,
		mask *globaldedicatedv1.ClusterApplyMask,
		validateOnly bool,
	) (*entity.Cluster, *entity.ClusterCustomStatus, error)
	RestoreCluster(
		ctx context.Context,
		restoreCluster *entity.Cluster,
		sourceClusterId uint64,
		restoreId uint64,
		validateOnly bool,
		clusterConfiguration *globaldedicatedv1.ClusterConfiguration,
	) (*entity.Cluster, error)
	GetLogRedactionPolicy(ctx context.Context, clusterId uint64) (*globaldedicatedv1.LogRedactionPolicy, error)
	UpdateLogRedactionPolicy(
		ctx context.Context,
		policy *globaldedicatedv1.LogRedactionPolicy,
	) (*globaldedicatedv1.LogRedactionPolicy, 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
	dedicatedCli     dedicatedglobal.DedicatedGlobal
	dataflowCli      dataflow.DataFlow
	accountCli       account.Client
	customStatusRepo repository.CustomStatusRepository
	encryptor        remoteencryptor.Client
	transaction      db.TransProvider
	workflow         workflowimpl.Workflow

	// FIXME(@yifan): cancel maintenance task execution when delete cluster, should be removed after new maintenance window is implemented
	mteRepo repository.MaintenanceTaskExecutionRepo
}

func NewService(
	clusterRepo repository.ClusterRepository,
	resourceTagRepo repository.ResourceTagRepository,
	bindingRepo repository.ClusterTagBindingRepository,
	customStatusRepo repository.CustomStatusRepository,
	dedicatedCli dedicatedglobal.DedicatedGlobal,
	dataflowCli dataflow.DataFlow,
	accountCli account.Client,
	encryptor remoteencryptor.Client,
	transaction db.TransProvider,
	workflow workflowimpl.Workflow,
	mteRepo repository.MaintenanceTaskExecutionRepo,
) (Service, error) {
	svc := &service{
		clusterRepo:      clusterRepo,
		resourceTagRepo:  resourceTagRepo,
		bindingRepo:      bindingRepo,
		customStatusRepo: customStatusRepo,
		dedicatedCli:     dedicatedCli,
		dataflowCli:      dataflowCli,
		accountCli:       accountCli,
		encryptor:        encryptor,
		transaction:      transaction,
		workflow:         workflow,
		mteRepo:          mteRepo,
	}

	return svc, nil
}

func cvtDedicatedCluster(base *entity.Cluster, dedicatedCluster *globaldedicatedv1.Cluster) *entity.Cluster {
	if dedicatedCluster == nil {
		return base
	}

	base.GlobalCluster = dedicatedCluster

	if dedicatedCluster.RegionDisplayName == "" {
		dedicatedCluster.RegionDisplayName = base.Region
	}

	return base
}
