package cluster

import (
	"context"

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

	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/adapter"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/service/cluster"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/featuregate"
)

var Tracer = otel.Tracer("app.cluster")

type service struct {
	clusterService cluster.Service
	billingFacade  adapter.BillingService

	trans             db.TransProvider
	clusterRepo       repository.ClusterRepo
	cloudProviderRepo repository.CloudProviderRepo

	featureGate featuregate.Service
}

func NewService(
	svc cluster.Service,
	billingFacade adapter.BillingService,
	trans db.TransProvider,
	clusterRepo repository.ClusterRepo,
	cloudProviderRepo repository.CloudProviderRepo,
	featureGate featuregate.Service,
) globalserverlessv1.ServerlessServiceServer {
	return &service{
		clusterService:    svc,
		billingFacade:     billingFacade,
		trans:             trans,
		clusterRepo:       clusterRepo,
		cloudProviderRepo: cloudProviderRepo,
		featureGate:       featureGate,
	}
}

func (c *service) Create(
	ctx context.Context,
	req *globalserverlessv1.ServerlessServiceCreateRequest,
) (*globalserverlessv1.ServerlessServiceCreateResponse, error) {
	ctx, span := Tracer.Start(ctx, "Create")
	defer span.End()

	providers, err := c.cloudProviderRepo.LoadAll(ctx)
	if err != nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "load cloud provider failed")
	}

	if !lo.ContainsBy(providers, func(item *entity.CloudProvider) bool {
		return item.Id.String() == req.CloudProvider
	}) {
		return nil, bizerr.New(bizerr.ErrCodeParams, "invalid cloud provider,name=%s", req.CloudProvider)
	}

	out, err := c.clusterService.Create(ctx, req)
	if err != nil {
		return nil, err
	}

	return &globalserverlessv1.ServerlessServiceCreateResponse{
		Cluster: out,
	}, nil
}

func (c *service) List(
	ctx context.Context,
	req *globalserverlessv1.ServerlessServiceListRequest,
) (*globalserverlessv1.ServerlessServiceListResponse, error) {
	ctx, span := Tracer.Start(ctx, "List")
	defer span.End()

	clusters, err := c.clusterService.List(ctx, req)
	if err != nil {
		return nil, err
	}

	return &globalserverlessv1.ServerlessServiceListResponse{
		Total: uint32(len(clusters)),
		Items: clusters,
	}, nil
}

func (c *service) Delete(
	ctx context.Context,
	req *globalserverlessv1.ServerlessServiceDeleteRequest,
) (*globalserverlessv1.ServerlessServiceDeleteResponse, error) {
	ctx, span := Tracer.Start(ctx, "Delete")
	defer span.End()

	resp, err := c.clusterService.Delete(ctx, req.ClusterId, req.GetValidateOnly())
	if err != nil {
		return nil, err
	}

	return &globalserverlessv1.ServerlessServiceDeleteResponse{
		Cluster: resp,
	}, nil
}

func (c *service) Update(
	ctx context.Context,
	req *globalserverlessv1.ServerlessServiceUpdateRequest,
) (*globalserverlessv1.ServerlessServiceUpdateResponse, error) {
	ctx, span := Tracer.Start(ctx, "Update")
	defer span.End()

	return c.clusterService.Update(ctx, req)
}

func (c *service) GetEncryptedCloudAdminPwd(
	ctx context.Context,
	req *globalserverlessv1.ServerlessServiceGetEncryptedCloudAdminPwdRequest,
) (*globalserverlessv1.ServerlessServiceGetEncryptedCloudAdminPwdResponse, error) {
	ctx, span := Tracer.Start(ctx, "GetEncryptedCloudAdminPwd")
	defer span.End()

	clusterEn, err := c.clusterRepo.Get(ctx, req.ClusterId, false)
	if err != nil {
		return nil, err
	}

	if clusterEn == nil {
		return nil, bizerr.New(bizerr.GrayscaleUpgradeDataNotInTargetSystem, "not found the cluster %s", req.ClusterId)
	}

	return &globalserverlessv1.ServerlessServiceGetEncryptedCloudAdminPwdResponse{
		EncryptedPasswd: clusterEn.EncryptedCloudAdminPassword,
	}, nil
}

func (c *service) CountClusters(
	ctx context.Context,
	req *globalserverlessv1.ServerlessServiceCountClustersRequest,
) (*globalserverlessv1.ServerlessServiceCountClustersResponse, error) {
	ctx, span := Tracer.Start(ctx, "CountClusters")
	defer span.End()

	count, err := c.clusterService.Count(ctx, req)
	if err != nil {
		return nil, err
	}

	return &globalserverlessv1.ServerlessServiceCountClustersResponse{Count: count}, nil
}

func (c *service) CheckCloudStoragePermission(
	ctx context.Context,
	req *globalserverlessv1.CheckCloudStoragePermissionRequest,
) (*globalserverlessv1.CheckCloudStoragePermissionResponse, error) {
	ctx, span := Tracer.Start(ctx, "CheckCloudStoragePermission")
	defer span.End()

	return c.clusterService.CheckCloudStoragePermission(ctx, req)
}

func (c *service) UpdateAuditLogConfig(
	ctx context.Context,
	req *globalserverlessv1.UpdateAuditLogConfigRequest,
) (*globalserverlessv1.UpdateAuditLogConfigResponse, error) {
	ctx, span := Tracer.Start(ctx, "UpdateAuditLogConfig")
	defer span.End()

	return c.clusterService.UpdateAuditLogConfig(ctx, req)
}
