package entity

import (
	"fmt"
	"strings"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

// Entities.
type (
	Spu        = globaldedicatedv1.Spu
	InstanceMu = globaldedicatedv1.InstanceMu
	StorageMu  = globaldedicatedv1.StorageMu
	Sku        = globaldedicatedv1.Sku

	VolumeType = globaldedicatedv1.StorageMu_VolumeType
	CpuArch    = globaldedicatedv1.InstanceMu_CpuArch

	SpuMapping struct {
		SpuId  uint64
		SkuKey string

		Provider  globalcommonv1.CloudProvider_Name
		Regions   map[globalcommonv1.Region_Name]bool
		TenantIDs map[uint64]bool
	}
)

func ParseSpuKey(spuKey string) (name string, componentType globaldedicatedv1.ComponentType, bizError bizerr.BizError) {
	parts := strings.Split(spuKey, ":")
	if len(parts) < 3 {
		return "", globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, bizerr.New(
			bizerr.ErrCodeUnknown,
			"invalid spu key format: %s",
			spuKey,
		)
	}

	name = parts[0]
	if parts[1] != "DEPLOY_TYPE_DEDICATED" {
		return "", globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, bizerr.New(
			bizerr.ErrCodeUnknown,
			"invalid spu key format, expect DEPLOY_TYPE_DEDICATED but got %s",
			parts[1],
		)
	}

	switch parts[2] {
	case "COMPONENT_TYPE_TIDB":
		componentType = globaldedicatedv1.ComponentType_TIDB
	case "COMPONENT_TYPE_TIKV":
		componentType = globaldedicatedv1.ComponentType_TIKV
	case "COMPONENT_TYPE_TIFLASH":
		componentType = globaldedicatedv1.ComponentType_TIFLASH
	case "COMPONENT_TYPE_PD":
		componentType = globaldedicatedv1.ComponentType_PD
	case "COMPONENT_TYPE_TIPROXY":
		componentType = globaldedicatedv1.ComponentType_TIPROXY
	default:
		return "", globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, bizerr.New(
			bizerr.ErrCodeUnknown,
			"unknown component type: %s",
			parts[2],
		)
	}

	return name, componentType, nil
}

func BuildSpuKey(componentType globaldedicatedv1.ComponentType, name, version string) string {
	k := fmt.Sprintf("%s:DEPLOY_TYPE_DEDICATED:%s", name, cvtComponentType(componentType))
	if version != "" {
		k = fmt.Sprintf("%s:%s", k, version)
	}
	return k
}

func cvtComponentType(componentType globaldedicatedv1.ComponentType) string {
	switch componentType {
	case globaldedicatedv1.ComponentType_TIDB:
		return "COMPONENT_TYPE_TIDB"
	case globaldedicatedv1.ComponentType_TIKV:
		return "COMPONENT_TYPE_TIKV"
	case globaldedicatedv1.ComponentType_TIFLASH:
		return "COMPONENT_TYPE_TIFLASH"
	case globaldedicatedv1.ComponentType_PD:
		return "COMPONENT_TYPE_PD"
	case globaldedicatedv1.ComponentType_TIPROXY:
		return "COMPONENT_TYPE_TIPROXY"
	default:
		tilog.PlainError("unknown component type", zap.String("componentType", componentType.String()))
		return "unknown_component"
	}
}
