package main

import (
	"fmt"
	"regexp"
	"strconv"
)

type tierUnitMap struct {
	Regions     []string `yaml:"regions,flow"`
	TenantIds   []uint64 `yaml:"tenantIds,flow"`
	TierUnitKey string   `yaml:"tierUnitKey"`
}

type tierUnitMapWithProvider struct {
	Provider string `yaml:"provider"`
	tierUnitMap
}

type pdTier struct {
	TierName    string `yaml:"tierName" validate:"min=1"`
	TierUnitKey string `yaml:"tierUnitKey" validate:"min=1"`
	NodeNum     int    `yaml:"nodeNum" validate:"gt=0"`
}

type tierCompute struct {
	InstanceType string `yaml:"type" validate:"min=1"`
}

type tierStorage struct {
	InstanceType  string `yaml:"type" validate:"oneof=gp2 gp3 pd-ssd local-storage"`
	Iops          int    `yaml:"iops" validate:"gte=0"`
	ThroughputMi  int    `yaml:"throughputMBs" validate:"gte=0"`
	MinSizeGi     int    `yaml:"minSizeGi" validate:"gte=0"`
	MaxSizeGi     int    `yaml:"maxSizeGi" validate:"gte=0,gtefield=MinSizeGi"`
	DefaultSizeGi int    `yaml:"defaultSizeGi" validate:"gtefield=MinSizeGi,ltefield=MaxSizeGi"`
}

type forMigration struct {
	TierName             string   `yaml:"tierName" validate:"min=1"`
	TierDisplayName      string   `yaml:"tierDisplayName" validate:"min=1"`
	NonStandardTenantIds []uint64 `yaml:"nonStandardTenantIds,flow"` // tenantId list
}

type tierYaml struct {
	ComponentType string                   `yaml:"component" validate:"oneof=pd tidb tikv tidb tiflash"`
	DeployType    string                   `yaml:"deployType" validate:"oneof=dedicated shared"`
	Name          string                   `yaml:"name" validate:"min=1"`
	DisplayName   string                   `yaml:"displayName" validate:"min=1"`
	Memo          string                   `yaml:"memo"`
	Default       bool                     `yaml:"default"`
	CpuNum        int                      `yaml:"cpu" validate:"gte=0"`      // cpuNum of devtier will be fixed 0
	MemoryGi      int                      `yaml:"memoryGi" validate:"gte=0"` // memoryGi of devtier will be fixed 0
	TierUnitMap   map[string][]tierUnitMap `yaml:"tierUnitMap"`               // key: aws, gcp
}

type tierUnitYaml struct {
	CloudProvider  string       `yaml:"provider" validate:"oneof=aws gcp"`
	ComponentType  string       `yaml:"component" validate:"oneof=pd tidb tikv tidb tiflash"`
	DeployType     string       `yaml:"deployType" validate:"oneof=dedicated shared"`
	Key            string       `yaml:"key" validate:"min=1"`
	Memo           string       `yaml:"memo"`
	DefaultNodeNum int          `yaml:"defaultNodeNum" validate:"gte=0"`
	Regions        []string     `yaml:"regions,flow"`
	Compute        tierCompute  `yaml:"compute"`
	Storage        *tierStorage `yaml:"storage"`
	PD             *pdTier      `yaml:"pd"`

	ForMigration *forMigration `yaml:"forMigration" validate:"required"`
}

func (m tierUnitMap) IsStandard() bool {
	return isStandard(m.TenantIds)
}

func isStandard(tenantIds []uint64) bool {
	return len(tenantIds) == 1 && tenantIds[0] == 0
}

var (
	CpuMemRe = regexp.MustCompile(`(\d+)C(\d+)G`)

	tierName2CpuNum = map[string]int{}
	tierName2Mem    = map[string]int{}
)

func parseCpuNumFromTierName(tierName string) (int, int) {
	result := CpuMemRe.FindStringSubmatch(tierName)
	if len(result) > 0 {
		cpu, err := strconv.ParseInt(result[1], 10, 64)
		if err != nil {
			panic(err)
		}
		mem, err := strconv.ParseInt(result[2], 10, 64)
		if err != nil {
			panic(err)
		}
		return int(cpu), int(mem)
	}
	return tierName2CpuNum[tierName], tierName2Mem[tierName]
}

func (node *tierUnitYaml) ToInstanceSKU(provider, region string) SKU {
	return SKU{
		Provider:     provider,
		Region:       region,
		ResourceType: "node",
		Price:        getInstancePrice(provider, region, node.Compute.InstanceType),
		ResourceDetail: ResourceDetail{
			InstanceType: node.Compute.InstanceType,
			Topology:     node.DeployType,
		},
		Category:    "Node Compute Cost",
		Description: fmt.Sprintf("The cost of the instance used by the node in %v %v", provider, region),
	}
}

func (d *tierUnitYaml) ToStorageSKUs(provider, region string) []SKU {
	skuStorage := SKU{
		Provider:     provider,
		Region:       region,
		ResourceType: "disk-storage",
		Price:        getDiskStoragePrice(provider, region, d.Storage.InstanceType),
		ResourceDetail: ResourceDetail{
			DiskType: d.Storage.InstanceType,
		},
		Category:    "Node Storage Cost",
		Description: fmt.Sprintf("tidb disk storage %v in %v %v", d.Storage.InstanceType, provider, region),
	}
	skus := []SKU{skuStorage}
	if d.Storage.InstanceType == "gp3" {
		skuIOPS := skuStorage
		skuIOPS.ResourceType = "disk-iops"
		skuIOPS.Price = getDiskIOPSPrice(provider, region, d.Storage.InstanceType)
		skuIOPS.MultipleFactor = &MultipleFactor{
			Label:     "iops",
			Threshold: 3000,
		}
		skuThroughput := skuStorage
		skuThroughput.ResourceType = "disk-throughput"
		skuThroughput.Price = getDiskThroughputPrice(provider, region, d.Storage.InstanceType)
		skuThroughput.MultipleFactor = &MultipleFactor{
			Label:     "throughput",
			Threshold: 125,
		}
		skus = append(skus, skuIOPS, skuThroughput)
	}

	if d.Storage.InstanceType == "io2" {
		skuIOPS := skuStorage
		skuIOPS.ResourceType = "disk-iops"
		skuIOPS.Price = getDiskIOPSPrice(provider, region, d.Storage.InstanceType)
		skuIOPS.MultipleFactor = &MultipleFactor{
			Label:     "iops",
			Threshold: 0, // io2 not need set Threshold
		}
		skuThroughput := skuStorage
		skuThroughput.ResourceType = "disk-throughput"
		skuThroughput.Price = getDiskThroughputPrice(provider, region, d.Storage.InstanceType)
		skus = append(skus, skuIOPS, skuThroughput)
	}

	return skus
}
