package nodepool

import "math"

const (
	// reservedMemory for system components and daemon pods
	reservedMemoryInMiB = 1536
)

// ref: https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture#node_allocatable
// and looks like Azure and EKS share the same algorithm https://github.com/awslabs/amazon-eks-ami/issues/387#issuecomment-582419188
// https://docs.microsoft.com/en-us/azure/aks/concepts-clusters-workloads#resource-reservations
var memTaxTable = []struct {
	boundary       uint64
	rate           float64
	quickDeduction uint64
}{
	{
		boundary:       1 * 1024, // 1GB
		rate:           0.0,
		quickDeduction: 255,
	},
	{
		boundary:       4 * 1024, // 4GB
		rate:           0.25,
		quickDeduction: 0,
	},
	{
		boundary:       8 * 1024, // 8GB
		rate:           0.2,
		quickDeduction: 205, // 0.05 * 4GB rounded
	},
	{
		boundary:       16 * 1024,
		rate:           0.1,
		quickDeduction: 1, // 0.1 * 4GB + 0.15 * 4GB
	},
	{
		boundary:       128 * 1024, // 128GB
		rate:           0.06,
		quickDeduction: 1680, // 0.19 * 4GB + 0.14 * 4GB + 0.04 * 8GB rounded
	},
	{
		boundary:       math.MaxInt64,
		rate:           0.02,
		quickDeduction: 6922, // (0.25 - 0.02) * 4 + (0.2 - 0.02) * 4 + (0.1 - 0.02) * 8 + (0.06 - 0.02) * 112 rounded
	},
}

// ComputeAvailableMemory computes the actual memory available to pods after reserving memory for system components and daemon pods.
func ComputeAvailableMemory(memoryInMiB uint64) uint64 {
	for _, level := range memTaxTable {
		// ASC order of memTaxTable is guaranteed
		if memoryInMiB <= level.boundary {
			memoryInMiB = memoryInMiB - uint64(math.Ceil(float64(memoryInMiB)*level.rate)) - level.quickDeduction
			break
		}
	}
	// subtract the reserved memory for daemon pods
	memoryInMiB -= reservedMemoryInMiB
	if memoryInMiB <= 0 {
		memoryInMiB = 0
	}
	return memoryInMiB
}
