package cluster

import (
	"fmt"
	"strconv"
	"strings"

	clusterng "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/devops/api"
	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	dedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	commonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

type CommonResp struct {
	api.BaseResp

	InstanceID *uint64 `json:"instance_id"` // the operation instance id
}

type ListClustersReq struct {
	api.PaginationReq

	TenantIDs  []string `form:"tenant_id" json:"tenant_id"`
	ProjectIDs []string `form:"project_id" json:"project_id"`
	ClusterIDs []string `form:"cluster_id" json:"cluster_id"`
	DeployType string   `form:"deploy_type" json:"deploy_type"`
	// List Resource Pools instead of custom clusters
	IsResourcePool bool   `form:"is_resource_pool" json:"is_resource_pool"`
	ServicePlan    string `form:"service_plan" json:"service_plan"`
}

type ListResourcePoolsReq struct {
	ResourcePoolIDs []string `form:"resource_pool_id" json:"resource_pool_id" collectionFormat:"multi"`
	View            string   `form:"view" json:"view" enums:"BASIC,DETAIL"`
	Page            uint32   `form:"page" json:"page"`
	PageSize        uint32   `form:"pagesize" json:"pagesize"`
}

type ListSharedResourcePoolsReq struct {
	ResourcePoolIDs []string `form:"resource_pool_id" json:"resource_pool_id" collectionFormat:"multi"`
	ServicePlan     string   `form:"service_plan" json:"service_plan"`
	Page            uint32   `form:"page" json:"page"`
	PageSize        uint32   `form:"pagesize" json:"pagesize"`
}

type ListClustersResp struct {
	Total    int64     `json:"total"`
	Clusters []Cluster `json:"items"`
}

type GetClusterResp struct {
	Cluster `json:",inline"`
}

type GetClusterComponentsResp struct {
	PD                 []*ComponentInfo `json:"pd"`
	TiKV               []*ComponentInfo `json:"tikv"`
	TiDB               []*ComponentInfo `json:"tidb"`
	TiFlash            []*ComponentInfo `json:"tiflash"`
	TiProxy            []*ComponentInfo `json:"tiproxy"`
	Cassandra          []*ComponentInfo `json:"cassandra"`
	TiKVStorageType    string           `json:"tikv_storage_type"`
	TiFlashStorageType string           `json:"tiflash_storage_type"`
}

type ComponentInfo struct {
	ComponentType        ComponentType                `json:"component_type"`
	TidbGroupId          string                       `json:"tidb_group_id"`
	IsDefaultTidbGroup   bool                         `json:"is_default_tidb_group"`
	TidbGroupDisplayName string                       `json:"tidb_group_display_name"`
	SpuName              string                       `json:"spu_name"`
	Count                uint32                       `json:"count"`
	Storages             map[string]*ComponentStorage `json:"storages"`
	SpuDisplayName       string                       `json:"spu_display_name"`
	InstanceType         string                       `json:"instance_type"`
	CpuNum               uint32                       `json:"cpu_num"`
	MemorySizeGi         uint32                       `json:"memory_size_gi"`
	InstanceCpuArch      string                       `json:"instance_cpu_arch"`
	Phase                string                       `json:"phase,omitempty"`
	HostID               string                       `json:"host_id,omitempty"`
	InternalEndpoint     string                       `json:"internal_endpoint,omitempty"`
	PrivatelinkDNSName   string                       `json:"privatelink_dns_name,omitempty"`
	PrivateLinkService   *PrivateLinkServiceInfo      `json:"private_link_service,omitempty"`
}

type PrivateLinkServiceInfo struct {
	Phase               string   `json:"phase"`
	ServiceName         string   `json:"service_name"`
	AvailabilityZoneIDs []string `json:"availability_zone_ids"`
}

type ComponentStorage struct {
	StorageId       uint64  `json:"storage_id"`
	ComponentId     uint64  `json:"component_id"`
	DiskType        string  `json:"disk_type"`
	SizeGi          uint32  `json:"size_gi"`
	Iops            uint32  `json:"iops"`
	ThroughputMibps uint32  `json:"throughput_mibps"`
	VolumeType      string  `json:"volume_type"`
	AdditionalIops  *uint32 `json:"additional_iops"`
}

type Cluster struct {
	ID                       uint64               `json:"id,string"`
	Name                     string               `json:"name"`
	Provider                 string               `json:"provider"`
	Region                   string               `json:"region"`
	LogicalRegion            string               `json:"logical_region,omitempty"`
	Profile                  string               `json:"profile"`
	Version                  string               `json:"version"`
	CreateTime               uint64               `json:"create_time"`
	ClusterComponent         ClusterComponent     `json:"cluster_components,omitempty"`
	Status                   string               `json:"status"`
	TenantID                 uint64               `json:"tenant_id,string"`
	TenantName               string               `json:"tenant_name"`
	ProjectID                uint64               `json:"project_id,string"`
	ProjectName              string               `json:"project_name"`
	IsDevTier                bool                 `json:"is_dev_tier"`
	CrossAccountDeploymentID uint64               `json:"cross_account_deployment_id"`
	ServerlessDetails        *ServerlessDetails   `json:"serverless_details,omitempty"` // only filled in get api, not in list api
	NextGenDetails           *NextGenDetails      `json:"nextgen_details,omitempty"`    // only filled in get api, not in list api
	ResourcePoolDetails      *ResourcePoolDetails `json:"resource_pool_details,omitempty"`
	IsNewArch                bool                 `json:"is_new_arch"` // set to true if the cluster is serverless
	DeployType               string               `json:"deploy_type"` // supported "DEPLOY_TYPE_NEXTGEN", "DEPLOY_TYPE_DEDICATED", "DEPLOY_TYPE_SERVERLESS". Empty string for cloud native
	ServicePlan              string               `json:"service_plan,omitempty"`
}

type ServerlessDetails struct {
	ResourceUsages
	ResourceConfig      ResourceConfig     `json:"resource_config"`
	Branches            []ServerlessBranch `json:"branches"`
	KeyspaceToken       string             `json:"keyspace_token"`
	KeyspaceID          uint32             `json:"keyspace_id"`
	EnableCustomerAlert bool               `json:"enable_customer_alert"`
}

type NextGenDetails struct {
	SharedPool     *SharedPool `json:"shared_pool,omitempty"` // only filled in get api, not in list api
	MacRCU         int64       `json:"max_rcu"`
	MinRCU         int64       `json:"min_rcu"`
	ResourcePoolID string      `json:"resource_pool_id,omitempty"`
	DataPlaneID    string      `json:"data_plane_id,omitempty"`
	TiDBTag        string      `json:"tidb_tag,omitempty"`
	TiKVGroupID    string      `json:"tikv_group_id,omitempty"`
}

type ResourcePoolDetails struct {
	SharedPool               *SharedPool      `json:"shared_pool,omitempty"` // only filled in get api, not in list api
	IsDefault                bool             `json:"is_default"`
	DefaultOrgs              []string         `json:"default_orgs,omitempty"`
	LogicalClusters          []LogicalCluster `json:"logical_clusters,omitempty"`
	InstanceLimit            uint64           `json:"instance_limit,omitempty"`
	VcpusLimit               uint64           `json:"vcpus_limit,omitempty"`
	TiKVTag                  string           `json:"tikv_tag,omitempty"`
	CIDR                     string           `json:"cidr,omitempty"`
	PdDeploymentMode         string           `json:"pd_deployment_mode,omitempty"`
	EnabledTiKVRemoteWorkers []string         `json:"enabled_tikv_remote_workers,omitempty"`
}

type SharedPool struct {
	ID              string                 `json:"id"`
	ComponentGroups []*SharedPoolComponent `json:"component_groups"`
	Phase           string                 `json:"phase"`
}

type SharedPoolComponent struct {
	// id means the component group id
	Id            string        `json:"id"`
	ComponentType ComponentType `json:"component_type"`
	CpuNum        uint32        `json:"cpu"`
	MemoryGi      uint32        `json:"memory_gi"`
	NodeCount     uint32        `json:"node_count"`
	MaxNodeCount  uint32        `json:"max_node_count"`
	MinNodeCount  uint32        `json:"min_node_count"`
	// include storage and tikv additional storage
	Storages []*SharedPoolStorage `json:"storages"`
	Version  string               `json:"version"`
	Phase    string               `json:"phase"`
}

type SharedPoolStorage struct {
	// the storage name
	Name string `json:"name,omitempty"`
	// the storage type
	// aws s3/io2 or gcp gcs
	// or azure blob
	Type string `json:"type,omitempty"`
	// the storage size
	SizeGi uint32 `json:"size_gi,omitempty"`
	// the storage iops
	Iops uint32 `json:"iops,omitempty"`
	// the storage throughput
	ThroughputMbs uint32 `json:"throughput_mbs,omitempty"`
}

type LogicalCluster struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	TiKVGroupID string `json:"tikv_group_id,omitempty"`
}

type ServerlessBranch struct {
	ID          string `json:"id"`
	DisplayName string `json:"display_name"`
	Status      string `json:"status"`
	CreateTime  int64  `json:"create_time"`
	UpdateTime  int64  `json:"update_time"`
}

type ResourceUsages struct {
	RequestUnit   uint64  `json:"request_unit"`
	RowStorage    float64 `json:"row_storage"`
	ColumnStorage float64 `json:"column_storage"`
}

type ResourceConfig struct {
	TiDBPool             string  `json:"tidb_pool"`
	RUMode               string  `json:"ru_mode"`
	MinTiDBReplicas      *uint32 `json:"min_tidb_replicas"`
	MaxTiDBReplicas      *uint32 `json:"max_tidb_replicas"`
	MinRCU               uint32  `json:"min_rcu"`
	MaxRCU               uint32  `json:"max_rcu"`
	ClusterPlan          string  `json:"cluster_plan"`
	Kubernetes           string  `json:"kubernetes"`
	ZoneRedundantEnabled bool    `json:"zone_redundant_enabled"`
}

type Component struct {
	Replicas    uint32                 `json:"replicas"`
	NodeProfile NodeProfileOfComponent `json:"node_profile"`
	MachineType string                 `json:"machineType"` // TODO(chenfei): remove. used by gitops to overlay configuration for component for now
	StorageType string                 `json:"storage_type"`
}

type ClusterComponent struct {
	PD      Component `json:"pd"`
	TiKV    Component `json:"tikv"`
	TiDB    Component `json:"tidb"`
	TiFlash Component `json:"tiflash"`
	TiProxy Component `json:"tiproxy"`
}

type NodeProfileCompute struct {
	CpuNum   int `json:"cpu"`
	MemoryMi int `json:"memory_mi"`
}

type NodeProfileOfComponent struct {
	Compute   NodeProfileCompute `json:"compute"`
	StorageGi int                `json:"storage_gi"`
	Name      string             `json:"name"`
}

type GetDMNamespaceResp struct {
	Namespace string   `json:"namespace"`
	PodNames  []string `json:"pod_names"`
}

type GetCloudIAMInfoResp struct {
	TiDBCloudAccountID         string `json:"tidb_cloud_account_id"`
	TiDBCloudAccountExternalID string `json:"tidb_cloud_account_external_id"`
	CloudProvider              string `json:"cloud_provider"`
	Region                     string `json:"region"`
	BucketName                 string `json:"bucket_name"`
	LogicalName                string `json:"logical_name"`
}

type VPCPeering struct {
	ID                     string `json:"id"`
	SourceCIDRBlock        string `json:"source_cidr_block"`
	DestinationCIDRBlock   string `json:"destination_cidr_block"`
	Provider               string `json:"provider"`
	Status                 string `json:"status"`
	SourceAccountID        string `json:"source_account_id"`
	SourceVPCID            string `json:"source_vpc_id"`
	DestinationAccountID   string `json:"destination_account_id"`
	DestinationVPCID       string `json:"destination_vpc_id"`
	VPCPeeringConnectionID string `json:"vpc_peering_connection_id"`
	DestinationRegion      string `json:"destination_region"`
}

type ListVPCPeeringsResp struct {
	VPCPeering []*VPCPeering `json:"items"`
}

type CreateVPCPeeringReq struct {
	CloudProvider  string `json:"cloud_provider" binding:"required"`
	CloudAccountID string `json:"cloud_account_id" binding:"required"`
	Region         string `json:"region" binding:"required"`
	VPCID          string `json:"vpc_id" binding:"required"`
	CIDR           string `json:"cidr" binding:"required"`
	Reason         string `json:"reason" binding:"required"`
}

type DeleteVPCPeeringReq struct {
	Reason string `json:"reason" binding:"required"`
}

func Build3LayerComponent(component *dedicatedv1.Cluster_ClusterComponent) Component {
	comp := Component{
		Replicas: pointer.Deref(component.Count, 0),
		NodeProfile: NodeProfileOfComponent{
			Compute: NodeProfileCompute{
				CpuNum:   int(component.CpuNum),
				MemoryMi: int(component.MemorySizeGi),
			},
			Name: component.SpuName,
		},
		MachineType: component.InstanceType,
	}
	if component.ComponentType == dedicatedv1.ComponentType_PD ||
		component.ComponentType == dedicatedv1.ComponentType_TIKV ||
		component.ComponentType == dedicatedv1.ComponentType_TIFLASH {
		if _, ok := component.Storages[dedicatedv1.Sku_DATA.String()]; ok {
			comp.NodeProfile.StorageGi = int(component.Storages[dedicatedv1.Sku_DATA.String()].SizeGi)
		}
	}
	return comp
}

func BuildClusterFromMgmt(cluster *mgmtv1.Cluster, projectName string) (Cluster, error) {
	if cluster == nil {
		return Cluster{}, fmt.Errorf("nil cluster")
	}

	out := Cluster{
		ID:          cluster.GetClusterId(),
		Name:        cluster.GetClusterName(),
		Provider:    cluster.GetCloudProvider(),
		Region:      cluster.GetRegion(),
		CreateTime:  uint64(cluster.GetCreateTime().Seconds),
		TenantID:    cluster.GetOrganizationId(),
		ProjectID:   cluster.GetProjectId(),
		ProjectName: projectName,
		DeployType:  cluster.GetDeployType().String(),
		IsNewArch:   true, // Assume new architecture for mgmt clusters
		ServicePlan: cluster.GetServicePlan().String(),
		// Version:
		// Status:
		// IsDevTier
	}
	if cluster.GetServicePlan() != commonv1beta1.ServicePlan_SERVICE_PLAN_UNSPECIFIED {
		out.ServicePlan = cluster.GetServicePlan().String()
	}

	switch cluster.GetDeployType() {
	case mgmtv1.DeployType_DEPLOY_TYPE_DEDICATED:
		dedicatedCluster := cluster.GetDedicatedCluster()
		out.Version = dedicatedCluster.GetVersion()
		out.Status = dedicatedCluster.State.String()

		for _, component := range dedicatedCluster.ClusterComponents {
			componentItem := Build3LayerComponent(component)
			if component.ComponentType == dedicatedv1.ComponentType_TIDB && !component.IsDefaultTidbGroup {
				// only return default TiDB group in this api
				continue
			}

			switch component.ComponentType {
			case dedicatedv1.ComponentType_PD:
				out.ClusterComponent.PD = componentItem
			case dedicatedv1.ComponentType_TIDB:
				out.ClusterComponent.TiDB = componentItem
			case dedicatedv1.ComponentType_TIKV:
				out.ClusterComponent.TiKV = componentItem
			case dedicatedv1.ComponentType_TIFLASH:
				out.ClusterComponent.TiFlash = componentItem
			case dedicatedv1.ComponentType_TIPROXY:
				out.ClusterComponent.TiProxy = componentItem
			default:
				return Cluster{}, fmt.Errorf("unsupported component type, tp=%s", component.ComponentType)
			}
		}
		// add tikv/tiflash storage type
		if len(cluster.TikvStorageType.String()) > 0 {
			out.ClusterComponent.TiKV.StorageType = cluster.TikvStorageType.String()
		}
		if cluster.TiflashStorageType != nil {
			out.ClusterComponent.TiFlash.StorageType = cluster.TiflashStorageType.String()
		}
	case mgmtv1.DeployType_DEPLOY_TYPE_SERVERLESS:
		serverlessC := cluster.GetServerlessCluster()
		out.Version = serverlessC.GetCluster().GetVersion()
		out.Status = serverlessC.GetUserStatus().String()
		out.IsDevTier = true
	case mgmtv1.DeployType_DEPLOY_TYPE_NEXTGEN:
		ngCluster := cluster.GetNextGenCluster()
		out.Version = ngCluster.GetVersion()
		out.Status = ngCluster.GetState().String()
		out.ServicePlan = ngCluster.GetServicePlan().String()
		out.NextGenDetails = &NextGenDetails{
			MacRCU:         ngCluster.GetMaxRcu(),
			MinRCU:         ngCluster.GetMinRcu(),
			ResourcePoolID: ngCluster.GetSharedPoolId(),
			DataPlaneID:    ngCluster.GetDataplaneId(),
		}
	}

	return out, nil
}

func BuildSharedPool(sharedPool *mgmthttpcli.SharedPool, hiddenComponents ...ComponentType) *SharedPool {
	apiSharedPool := &SharedPool{
		ID:              sharedPool.Id,
		Phase:           sharedPool.Phase,
		ComponentGroups: make([]*SharedPoolComponent, 0, len(sharedPool.ComponentGroups)),
	}
	hidden := make(map[string]struct{}, len(hiddenComponents))
	for _, component := range hiddenComponents {
		hidden[strings.ToLower(string(component))] = struct{}{}
	}
	for _, group := range sharedPool.ComponentGroups {
		cmp := strings.ToLower(group.ComponentType)
		if _, ok := hidden[cmp]; ok {
			continue
		}
		apiGroup := &SharedPoolComponent{
			Id:            group.Id,
			ComponentType: ComponentType(group.ComponentType),
			Version:       group.Version,
			Phase:         group.Phase,
		}
		if group.NodeQuota != nil {
			apiGroup.NodeCount = group.NodeQuota.NodeCount
			apiGroup.MaxNodeCount = group.NodeQuota.MaxNodeCount
			apiGroup.MinNodeCount = group.NodeQuota.MinNodeCount
			if group.NodeQuota.NodeSpec != nil {
				apiGroup.CpuNum = group.NodeQuota.NodeSpec.Cpu
				apiGroup.MemoryGi = group.NodeQuota.NodeSpec.MemoryGi
			}
		}
		for _, storage := range group.Storages {
			apiGroup.Storages = append(apiGroup.Storages, &SharedPoolStorage{
				Name:          storage.Name,
				Type:          storage.Type,
				SizeGi:        storage.SizeGi,
				Iops:          storage.Iops,
				ThroughputMbs: storage.ThroughputMbs,
			})
		}
		apiSharedPool.ComponentGroups = append(apiSharedPool.ComponentGroups, apiGroup)
	}

	return apiSharedPool
}

func BuildClusterFromResourcePool(rp *nextgenv1.ResourcePoolProfile) Cluster {
	rpp := rp.GetResourcePool()
	result := Cluster{
		ID:            rpp.GetId(),
		Name:          rpp.GetName(),
		Provider:      rpp.GetProviderId().String(),
		Region:        rpp.GetRegionId().String(),
		LogicalRegion: rp.GetRegion().GetLogicalName(),
		Version:       rp.Version,
		CreateTime:    uint64(rpp.GetCreateTime().Seconds),
		DeployType:    "DEPLOY_TYPE_RESOURCE_POOL",
		ServicePlan:   rpp.GetServicePlan().String(),
		IsNewArch:     true,
		ResourcePoolDetails: &ResourcePoolDetails{
			IsDefault:     rp.IsDefault,
			DefaultOrgs:   rp.GetDefaultOrgs(),
			InstanceLimit: rpp.InstanceLimit,
			VcpusLimit:    rpp.VcpusLimit,
			CIDR:          rpp.Cidr,
		},
	}
	if rpp.GetOwnerType() == nextgenv1.ResourcePool_ORGANIZATION {
		result.TenantID = rpp.GetOwnerId()
	}
	if mode := rp.GetPdDeploymentMode(); mode != nextgenv1.PdDeploymentMode_PD_DEPLOYMENT_MODE_UNSPECIFIED {
		result.ResourcePoolDetails.PdDeploymentMode = mode.String()
	}
	for _, worker := range rp.GetEnabledTikvRemoteWorkers() {
		result.ResourcePoolDetails.EnabledTiKVRemoteWorkers = append(result.ResourcePoolDetails.EnabledTiKVRemoteWorkers, worker.String())
	}
	for _, i := range rp.GetInstances() {
		result.ResourcePoolDetails.LogicalClusters = append(result.ResourcePoolDetails.LogicalClusters, LogicalCluster{
			ID:   strconv.FormatUint(i.GetClusterId(), 10),
			Name: i.GetName(),
		})
	}
	return result
}

type ComponentType string

const (
	ComponentTypeTikv      ComponentType = "tikv"
	ComponentTypeTidb      ComponentType = "tidb"
	ComponentTypeTiflash   ComponentType = "tiflash"
	ComponentTypePd        ComponentType = "pd"
	ComponentTypeTiproxy   ComponentType = "tiproxy"
	ComponentTypeTicdc     ComponentType = "ticdc"
	ComponentTypeCassandra ComponentType = "cassandra"

	// for nextgen shared pool
	/*
		ComponentTypeTiflashLearner    ComponentType = "tiflash_learner"
		ComponentTypeTikvWorker        ComponentType = "tikv_worker"
		ComponentTypeCoprocessorWorker ComponentType = "coprocessor_worker"
		ComponentTypeTidbWorker        ComponentType = "tidb_worker"
		ComponentTypePdTso             ComponentType = "pd_tso"
		ComponentTypePdScheduler       ComponentType = "pd_scheduler"
		ComponentTypeTiflashWrite      ComponentType = "tiflash_write"
		ComponentTypeTiflashCompute    ComponentType = "tiflash_compute"
	*/
)

func APIComponentTypeFromDedicated(componentType dedicatedv1.ComponentType) ComponentType {
	switch componentType {
	case dedicatedv1.ComponentType_TIDB:
		return ComponentTypeTidb
	case dedicatedv1.ComponentType_TIKV:
		return ComponentTypeTikv
	case dedicatedv1.ComponentType_TIFLASH:
		return ComponentTypeTiflash
	case dedicatedv1.ComponentType_PD:
		return ComponentTypePd
	case dedicatedv1.ComponentType_TIPROXY:
		return ComponentTypeTiproxy
	default:
		return ComponentType(dedicatedv1.ComponentType_name[int32(componentType)])
	}
}

func DedicatedComponentTypeFromAPI(componentType ComponentType) (dedicatedv1.ComponentType, error) {
	switch componentType {
	case ComponentTypeTidb:
		return dedicatedv1.ComponentType_TIDB, nil
	case ComponentTypeTikv:
		return dedicatedv1.ComponentType_TIKV, nil
	case ComponentTypeTiflash:
		return dedicatedv1.ComponentType_TIFLASH, nil
	case ComponentTypePd:
		return dedicatedv1.ComponentType_PD, nil
	case ComponentTypeTiproxy:
		return dedicatedv1.ComponentType_TIPROXY, nil
	default:
		return dedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, fmt.Errorf("invalid component_type: %s", componentType)
	}
}

func BuildCassandraComponentInfo(cassandra *clusterng.GetCassandraProxyResponse) *ComponentInfo {
	if cassandra == nil {
		return nil
	}

	info := &ComponentInfo{
		ComponentType:      ComponentTypeCassandra,
		Phase:              cassandra.GetPhase().String(),
		HostID:             cassandra.GetHostId(),
		InternalEndpoint:   cassandra.GetInternalEndpoint(),
		PrivatelinkDNSName: cassandra.GetPrivatelinkDnsName(),
	}

	nodeQuota := cassandra.GetNodeQuota()
	if nodeQuota != nil {
		info.Count = nodeQuota.GetNodeCount()
		if nodeSpec := nodeQuota.GetNodeSpec(); nodeSpec != nil {
			info.CpuNum = nodeSpec.GetCpu()
			info.MemorySizeGi = nodeSpec.GetMemoryGi()
			info.InstanceCpuArch = nodeSpec.GetArch()
		}
	}

	privateLinkService := cassandra.GetPrivateLinkService()
	if privateLinkService != nil {
		info.PrivateLinkService = &PrivateLinkServiceInfo{
			Phase:               privateLinkService.GetPhase().String(),
			ServiceName:         privateLinkService.GetServiceName(),
			AvailabilityZoneIDs: privateLinkService.GetAvailabilityZoneIds(),
		}
	}

	return info
}

func BuildDedicatedComponentsDetail(components []*dedicatedv1.Cluster_ClusterComponent) GetClusterComponentsResp {
	tidb := make([]*ComponentInfo, 0)
	tikv := make([]*ComponentInfo, 0)
	tiflash := make([]*ComponentInfo, 0)
	pd := make([]*ComponentInfo, 0)
	tiproxy := make([]*ComponentInfo, 0)
	cassandra := make([]*ComponentInfo, 0)
	for _, c := range components {
		info := &ComponentInfo{
			SpuName:              c.SpuName,
			Count:                pointer.Deref(c.Count, 0),
			TidbGroupId:          strconv.FormatUint(c.TidbGroupId, 10),
			IsDefaultTidbGroup:   c.IsDefaultTidbGroup,
			TidbGroupDisplayName: c.TidbGroupDisplayName,
			SpuDisplayName:       c.SpuDisplayName,
			InstanceType:         c.InstanceType,
			CpuNum:               c.CpuNum,
			MemorySizeGi:         c.MemorySizeGi,
			InstanceCpuArch:      c.InstanceCpuArch.String(),
		}

		storages := make(map[string]*ComponentStorage)
		for k, v := range c.Storages {
			storages[k] = &ComponentStorage{
				StorageId:       v.StorageId,
				ComponentId:     v.ComponentId,
				DiskType:        v.DiskType.String(),
				SizeGi:          v.SizeGi,
				Iops:            v.Iops,
				ThroughputMibps: v.ThroughputMibps,
				VolumeType:      v.GetVolumeType().String(),
				AdditionalIops:  v.AdditionalIops,
			}
		}
		info.Storages = storages

		info.ComponentType = APIComponentTypeFromDedicated(c.ComponentType)
		switch info.ComponentType {
		case ComponentTypeTidb:
			tidb = append(tidb, info)
		case ComponentTypeTikv:
			tikv = append(tikv, info)
		case ComponentTypeTiflash:
			tiflash = append(tiflash, info)
		case ComponentTypePd:
			pd = append(pd, info)
		case ComponentTypeTiproxy:
			tiproxy = append(tiproxy, info)
		}
	}
	return GetClusterComponentsResp{
		PD:        pd,
		TiDB:      tidb,
		TiKV:      tikv,
		TiFlash:   tiflash,
		TiProxy:   tiproxy,
		Cassandra: cassandra,
	}
}

const (
	ProviderAWS      string = "aws"
	ProviderAliCloud string = "alicloud"
)

func BuildVPCPeerings(resp *mgmthttpcli.ListVPCPeeringsResp) *ListVPCPeeringsResp {
	if resp == nil {
		return &ListVPCPeeringsResp{}
	}

	apiResp := &ListVPCPeeringsResp{
		VPCPeering: make([]*VPCPeering, 0, len(resp.VPCPeerings)),
	}

	for _, vp := range resp.VPCPeerings {
		if vp == nil {
			continue
		}

		vpcPeering := &VPCPeering{
			ID:                   vp.ID,
			SourceCIDRBlock:      vp.SourceCIDRBlock,
			DestinationCIDRBlock: vp.DestinationCIDRBlock,
			Status:               vp.Status,
		}

		info := vp.AWS
		provider := ProviderAWS
		if info == nil && vp.Ali != nil {
			info = vp.Ali
			provider = ProviderAliCloud
		}

		if info != nil {
			vpcPeering.Provider = provider
			vpcPeering.SourceAccountID = info.SourceAccountID
			vpcPeering.SourceVPCID = info.SourceVPCID
			vpcPeering.DestinationAccountID = info.DestinationAccountID
			vpcPeering.DestinationVPCID = info.DestinationVPCID
			vpcPeering.VPCPeeringConnectionID = info.VPCPeeringConnectionID
			vpcPeering.DestinationRegion = info.DestinationRegion
		}
		apiResp.VPCPeering = append(apiResp.VPCPeering, vpcPeering)
	}

	return apiResp
}
