package weightrand

import (
	"errors"
	"fmt"
	"math/rand"
	"slices"
)

// WeightedItem 结构体，用于存储项目和其权重
type WeightedItem[T comparable] struct {
	Item   T
	Weight int64
}

// WeightedRandomSelector 结构体，用于根据权重随机选择一项
type WeightedRandomSelector[T comparable] struct {
	Items []*WeightedItem[T]
}

// NewWeightedRandomSelector 创建一个新的 WeightedRandomSelector 实例
func NewWeightedRandomSelector[T comparable](cnt int) *WeightedRandomSelector[T] {
	wrs := &WeightedRandomSelector[T]{
		Items: make([]*WeightedItem[T], 0, cnt),
	}
	return wrs
}

// AddItem 添加一个新的权重项
func (wrs *WeightedRandomSelector[T]) AddItem(item T, weight int64) {
	if weight <= 0 {
		return
	}
	it := new(WeightedItem[T])
	it.Item = item
	it.Weight = weight
	wrs.Items = append(wrs.Items, it)
}

// RemoveItem 删除指定的权重项
func (wrs *WeightedRandomSelector[T]) RemoveItem(item T) {
	wrs.Items = slices.DeleteFunc(wrs.Items, func(wi *WeightedItem[T]) bool {
		return wi.Item == item
	})
}

// SelectRandom 根据权重随机选择一项
func (wrs *WeightedRandomSelector[T]) SelectRandom() (T, error) {
	var t T
	if len(wrs.Items) == 0 {
		return t, errors.New("empty items")
	}

	// 计算总权重
	totalWeight := int64(0)
	for _, item := range wrs.Items {
		totalWeight += item.Weight
	}
	if totalWeight <= 0 {
		return t, errors.New("total weight is zero")
	}

	// 生成一个 [0, totalWeight) 之间的随机数
	randomValue := rand.Int63n(totalWeight)

	// 根据随机数找到对应的项目
	for _, item := range wrs.Items {
		if randomValue < item.Weight {
			return item.Item, nil
		}
		randomValue -= item.Weight
	}

	// 理论上不会到达这里，因为总权重一定会匹配到某个项目
	return t, errors.New("unreachable code")
}

func test() {
	items := []WeightedItem[string]{
		{"Item1", 10},
		{"Item2", 20},
		{"Item3", 30},
		{"Item4", 40},
	}

	selector := NewWeightedRandomSelector[string](len(items))
	for i := 0; i < 10; i++ {
		fmt.Println(selector.SelectRandom())
	}
}
