package util

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/tidbcloud/infra-provider/pkg/consts"
)

var (
	adjustableIOPSVolumeTypes = map[string]bool{
		consts.AWSVolumeTypeGP2:               false,
		consts.AWSVolumeTypeGP3:               true,
		consts.AWSVolumeTypeIO1:               true,
		consts.AWSVolumeTypeIO2:               true,
		consts.GCPVolumeTypePDSSD:             false,
		consts.AZUREVolumeTypePremiumV2:       true,
		consts.AZUREVolumeTypeUltraSSD:        true,
		consts.GCPVolumeTypeHyperdiskBalanced: true,
	}
	adjustableThroughputVolumeTypes = map[string]bool{
		consts.AWSVolumeTypeGP2:               false,
		consts.AWSVolumeTypeGP3:               true,
		consts.AWSVolumeTypeIO1:               false,
		consts.AWSVolumeTypeIO2:               false,
		consts.GCPVolumeTypePDSSD:             false,
		consts.AZUREVolumeTypePremiumV2:       true,
		consts.AZUREVolumeTypeUltraSSD:        true,
		consts.GCPVolumeTypeHyperdiskBalanced: true,
	}

	// Azure:
	// pd-premium2-3000-400
	// tikv-premium2-3000-400
	// AWS:
	// tikv-gp3-3000-400
	// tikv-io1-3000-400
	// tikv-io2-3000-400
	knownFourPartsVolumeTypes = map[string]bool{
		consts.AWSVolumeTypeGP3:               true,
		consts.AWSVolumeTypeIO1:               true,
		consts.AWSVolumeTypeIO2:               true,
		consts.AZUREVolumeTypePremiumV2:       true,
		consts.AZUREVolumeTypeUltraSSD:        true,
		consts.GCPVolumeTypeHyperdiskBalanced: true,
	}
)

func GetTypeIOPSThroughputFromClassName(storageClassName string) (volType string, iops, throughput uint32) {
	strs := strings.Split(storageClassName, "-")

	if len(strs) == 3 { // tikv-pd-ssd, gcp pd-ssd disk
		if strings.Join(strs[1:], "-") != consts.GCPVolumeTypePDSSD {
			// log.Errorf("unsupported volume type %s", name)
			return "", 0, 0
		}
		return consts.GCPVolumeTypePDSSD, 0, 0
	} else if len(strs) == 2 { // tiflash-gp2
		if strs[1] == consts.AZUREVolumeTypeStandard_LRS_ALIAS {
			return consts.AZUREVolumeTypeStandard_LRS_ALIAS, 0, 0
		}

		if strs[1] == consts.AWSVolumeTypeGP2 {
			return consts.AWSVolumeTypeGP2, 0, 0
		}

		// Unsupported volume type, eg. index-acceleration
		return "", 0, 0

	} else if len(strs) == 5 && strings.Contains(storageClassName, "hyperdisk-balanced") {
		// GCP:
		// tikv-hyperdisk-balanced-3000-400
		volType = fmt.Sprintf("%s-%s", strs[1], strs[2])
		iopsUint64, err := strconv.ParseUint(strs[3], 10, 32)
		if err != nil {
			return "", 0, 0
		}
		throughputUint64, err := strconv.ParseUint(strs[4], 10, 32)
		if err != nil {
			return "", 0, 0
		}

		return volType, uint32(iopsUint64), uint32(throughputUint64)
	} else if len(strs) >= 4 {
		// Azure:
		// pd-premium2-3000-400
		// tikv-premium2-3000-400
		// AWS:
		// tikv-gp3-3000-400
		// tikv-io1-3000-400
		// tikv-io2-3000-400
		// AWS with CMEK:
		// tikv-gp3-8000-594-906324658258-key-1c33ece5-b50e-4728-bbae-0b4bf0b24401
		if !knownFourPartsVolumeTypes[strs[1]] {
			return "", 0, 0
		}

		// if iops or throughput is not adjustable, ignore it, it will be set to 0
		var err error
		var iopsUint64 uint64
		if adjustableIOPSVolumeTypes[strs[1]] {
			iopsUint64, err = strconv.ParseUint(strs[2], 10, 32)
			if err != nil {
				return "", 0, 0
			}
		}

		var throughputUint64 uint64
		if adjustableThroughputVolumeTypes[strs[1]] {
			throughputUint64, err = strconv.ParseUint(strs[3], 10, 32)
			if err != nil {
				return "", 0, 0
			}
		}
		return strs[1], uint32(iopsUint64), uint32(throughputUint64)
	}

	return "", 0, 0
}

// GCPHyperdiskPerformanceChanged reports whether a StorageClass update changes
// the provisioned IOPS or throughput of an existing Hyperdisk Balanced volume.
func GCPHyperdiskPerformanceChanged(currentStorageClass, desiredStorageClass string) bool {
	if currentStorageClass == "" || desiredStorageClass == "" || currentStorageClass == desiredStorageClass {
		return false
	}

	currentType, currentIOPS, currentThroughput := GetTypeIOPSThroughputFromClassName(currentStorageClass)
	desiredType, desiredIOPS, desiredThroughput := GetTypeIOPSThroughputFromClassName(desiredStorageClass)
	return currentType == consts.GCPVolumeTypeHyperdiskBalanced &&
		desiredType == consts.GCPVolumeTypeHyperdiskBalanced &&
		(currentIOPS != desiredIOPS || currentThroughput != desiredThroughput)
}
