package grpcli

import (
	"context"
	"sync"

	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/config"
	"github.com/tidbcloud/metrics-integration/global/pkg/log"
	"github.com/tidbcloud/pkgv2/bizerr"
	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	dedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	v1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	mgmt "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"
)

var MgntDeletedClusterStatus = []v1beta1.ClusterState{
	v1beta1.ClusterState_DELETED, v1beta1.ClusterState_DELETING,
}

var (
	mgmtClient  mgmt.ClusterServiceClient
	mgmtSvcOnce sync.Once
)

func getMgmtSvcClient() mgmt.ClusterServiceClient {
	mgmtSvcOnce.Do(func() {
		ctx := context.Background()
		addr := config.Global().GrpcConfig.MgmtSvcAddr
		logger := log.GetLogger(ctx)
		conn, err := tigrpc.NewClientConn(addr, tigrpc.WithDefault())
		logger.Info("Initializing mgmt service client", zap.Any("err", err), zap.String("addr", addr))
		if err != nil {
			logger.Fatal("initialize client failed", zap.String("addr", addr), zap.Error(err))
		}
		mgmtClient = mgmt.NewClusterServiceClient(conn)
	})

	return mgmtClient
}

func GetClusterByID(ctx context.Context, clusterID uint64) (*mgmt.Cluster, bizerr.BizError) {
	client := getMgmtSvcClient()
	req := &mgmt.ListClustersRequest{
		Filter: &mgmt.ClusterFilter{
			ClusterIds:    []uint64{clusterID},
			ExcludeStates: MgntDeletedClusterStatus,
			View:          mgmt.ClusterFilter_CLUSTER_VIEW_FULL,
		},
	}
	listResp, err := client.ListClusters(ctx, req)
	if err != nil {
		return nil, bizerr.WrapError(err, "GetCluster failed")
	}
	if len(listResp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterID)
	}
	return listResp.Clusters[0], nil
}

func GetDedicatedClusterByID(ctx context.Context, clusterID uint64) (*dedicatedv1.Cluster, bizerr.BizError) {
	cluster, err := GetClusterByID(ctx, clusterID)
	if err != nil {
		return nil, bizerr.WrapError(err, "GetDedicatedClusterByID failed")
	}
	dedicatedCluster := cluster.GetDedicatedCluster()
	return dedicatedCluster, nil
}

// GetClusterServicePlan returns the service plan string for a cluster.
// For NextGen clusters it uses the ServicePlan proto field;
// for Serverless clusters (Essential/Starter) it reads the annotation;
// for Dedicated clusters it returns "Dedicated".
func GetClusterServicePlan(cluster *mgmt.Cluster) string {
	switch cluster.GetDeployType() {
	case mgmt.DeployType_DEPLOY_TYPE_DEDICATED:
		return "Dedicated"
	case mgmt.DeployType_DEPLOY_TYPE_NEXTGEN:
		switch cluster.GetNextGenCluster().GetServicePlan() {
		case commonv1.ServicePlan_Premium:
			return commonv1.ServicePlan_Premium.String()
		case commonv1.ServicePlan_Essential_V2:
			return commonv1.ServicePlan_Essential_V2.String()
		case commonv1.ServicePlan_Essential:
			return commonv1.ServicePlan_Essential.String()
		case commonv1.ServicePlan_Starter:
			return commonv1.ServicePlan_Starter.String()
		case commonv1.ServicePlan_BYOC:
			return commonv1.ServicePlan_BYOC.String()
		}
	case mgmt.DeployType_DEPLOY_TYPE_SERVERLESS:
		if sl := cluster.GetServerlessCluster(); sl != nil {
			if annotations := sl.GetCluster().GetAnnotations(); annotations != nil {
				return annotations["free-tier.tidbcloud.com/service-plan"]
			}
		}
	}
	return ""
}
