package otherutils

import (
	"errors"
	"math/rand"
)

func init() {
	// rand.Seed(time.Now().UnixNano())
}

// RandInterval 随机1个数
func RandInterval(b1, b2 int64) int64 {
	min, max := normalizeRange(b1, b2)
	return min + rand.Int63n(max-min+1)
}

// RandInterval64 随机1个数
func RandInterval64(b1, b2 int64) int64 {
	min, max := normalizeRange64(b1, b2)
	return min + rand.Int63n(max-min+1)
}

func normalizeRange64(b1, b2 int64) (min, max int64) {
	if b1 < b2 {
		return b1, b2
	}
	return b2, b1
}

// RandIntervalN 随机生成n个范围在[b1, b2]之间的唯一整数
// 使用Fisher-Yates洗牌算法
func RandIntervalN(b1, b2 int64, n int64) []int64 {
	min, max := normalizeRange(b1, b2)
	rangeSize := max - min + 1
	n = limitCount(n, int64(rangeSize))

	if n == 1 {
		return []int64{min + rand.Int63n(max-min+1)}
	}

	return generateUniqueRandomNumbers(min, max, n)
}

func normalizeRange(b1, b2 int64) (min, max int64) {
	if b1 < b2 {
		return b1, b2
	}
	return b2, b1
}

func limitCount(n int64, rangeSize int64) int64 {
	if n > rangeSize {
		return rangeSize
	}
	return n
}

func generateUniqueRandomNumbers(min, max int64, n int64) []int64 {
	result := make([]int64, n)
	usedNumbers := make(map[int64]int64)
	rangeSize := max - min + 1

	for i := int64(0); i < n; i++ {
		randomValue := min + rand.Int63n(rangeSize)

		if replacementValue, exists := usedNumbers[randomValue]; exists {
			result[i] = replacementValue
		} else {
			result[i] = randomValue
		}

		lastValue := max - i
		if randomValue != lastValue {
			if replacementValue, exists := usedNumbers[lastValue]; exists {
				usedNumbers[randomValue] = replacementValue
			} else {
				usedNumbers[randomValue] = lastValue
			}
		}

		rangeSize--
	}

	return result
}

type Number interface {
	int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64
}

// RandOneNum 随机一个数字
func RandOneNum[T Number](min, max T) T {
	if min == max {
		return min
	}

	if min > max {
		min, max = max, min
	}
	var result T
	switch any(min).(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		result = T(rand.Int63n(int64(max-min)) + int64(min))
	case float32, float64:
		result = T(rand.Float64()*(float64(max)-float64(min)) + float64(min))
	default:
		panic("invalid type")
	}

	return result
}

// RandChoiceByWeighted 根据权重选取一个元素 [[id,weight]]
func RandChoiceByWeighted(items [][]int64) int64 {
	var totalWeight int64
	for _, item := range items {
		totalWeight += item[1]
	}
	if totalWeight <= 0 {
		return -1
	}
	// 生成一个0到总权重之间的随机数
	randomNumber := rand.Int63n(totalWeight) + 1

	// 累积权重，直到累积权重超过随机数
	var cumulativeWeight int64
	for _, item := range items {
		cumulativeWeight += item[1]
		if randomNumber <= cumulativeWeight {
			return item[0]
		}
	}

	// 如果没有找到（理论上不会发生），返回0
	return -1
}

// type WeightedItem interface {
// 	GetWeight() int64
// }

type WeightedTable[T any] struct {
	items       []T
	totalWeight int64
	getWeight   func(T) int64
}

func SetWeightedTable[T any](items []T, getWeight func(T) int64) *WeightedTable[T] {
	var totalWeight int64
	for _, item := range items {
		totalWeight += getWeight(item) //item.GetWeight()
	}
	return &WeightedTable[T]{
		items:       items,
		totalWeight: totalWeight,
		getWeight:   getWeight,
	}
}

func (rt *WeightedTable[T]) RandOneItem(reCalcWeight bool) (val T, err error) {
	var item T
	if reCalcWeight {
		var totalWeight int64
		for _, item := range rt.items {
			totalWeight += rt.getWeight(item)
		}
		rt.totalWeight = totalWeight
	}

	if rt.totalWeight <= 0 {
		return item, errors.New("total weight is zero")
	}
	// 生成一个0到总权重之间的随机数
	randomNumber := rand.Int63n(rt.totalWeight) + 1

	// 累积权重，直到累积权重超过随机数
	var cumulativeWeight int64
	for _, item := range rt.items {
		cumulativeWeight += rt.getWeight(item)
		if randomNumber <= cumulativeWeight {
			return item, nil
		}
	}

	// 如果没有找到（理论上不会发生），返回0
	return item, errors.New("not found")
}
