package test

import (
	"fmt"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/entity"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/service/collector"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/strategy"
	"math/rand"
	"testing"
	"time"
)

func TestReplication(t *testing.T) {
	parts := []entity.Participant{
		{Name: "test1", Participant_id: 1},
		{Name: "test2", Participant_id: 2},
		{Name: "test3", Participant_id: 3},
	}
	rsc := []*collector.ResourceStats{
		{
			ClusterId: "1",
			Name:      "test1",
		},
		{
			ClusterId: "2",
			Name:      "test2"},
		{
			ClusterId: "3",
			Name:      "test3"},
	}
	tests := []struct {
		name    string
		replica int32
		ps      []entity.Participant
		res     []*collector.ResourceStats
	}{
		{
			name:    "test1",
			replica: 1,
			ps:      parts,
		},
		{
			name:    "test2",
			replica: 2,
			ps:      parts,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var clusterIds []string
			for _, stats := range rsc {
				clusterIds = append(clusterIds, stats.ClusterId)
			}
			repl := strategy.NewReplicationStrategy(clusterIds, 0)
			schedule, err := repl.Schedule()
			if err != nil {
				return
			}
			for _, cluster := range schedule {
				fmt.Println(cluster)
			}

		})
	}

}

func TestStaticWeight(t *testing.T) {
	parts := map[string]int32{
		"test1": 6,
		"test2": 5,
		"test3": 2,
	}
	tests := []struct {
		name    string
		replica int32
		ps      map[string]int32
	}{
		{
			name:    "test1",
			replica: 1,
			ps:      parts,
		},
		{
			name:    "test2",
			replica: 5,
			ps:      parts,
		},
		{
			name:    "test2",
			replica: 6,
			ps:      parts,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			repl := strategy.NewStaticWeightStrategy(tt.ps, tt.replica)
			schedule, err := repl.Schedule()
			if err != nil {
				return
			}
			for _, cluster := range schedule {
				fmt.Println(cluster)
			}

		})
	}
}

func TestRandom(t *testing.T) {
	// 使用当前时间作为随机数种子，确保每次程序运行产生的随机数序列都不同
	rand.Seed(time.Now().UnixNano())

	/*randomNum := randInt(1, 100)
	fmt.Println("Random number:", randomNum)*/
	total := 5 // 假设总数是5
	first, second := splitIntoTwoRandomParts(total)
	fmt.Printf("第一部分的数量: %d, 第二部分的数量: %d\n", first, second)
}

// randInt 生成一个指定范围内的随机整数，包括min但不包括max
func randInt(min, max int) int {
	return min + rand.Intn(max-min)
}

func splitIntoTwoRandomParts(total int) (int, int) {
	if total < 2 {
		// 如果总数小于2，则无法分成两部分
		return 0, 0
	}
	// 生成一个随机数作为第一部分的数量（范围在[1, total-1]之间）
	firstPart := rand.Intn(total-1) + 1
	// 第二部分的数量就是总数减去第一部分的数量
	secondPart := total - firstPart
	return firstPart, secondPart
}

func splitIntoRandomParts(total int) (int, int) {
	if total < 2 {
		// 如果总数小于2，则无法分成两部分
		return 0, 0
	}
	// 生成一个随机数作为第一部分的数量（范围在[1, total-1]之间）
	firstPart := rand.Intn(total-1) + 1
	// 第二部分的数量就是总数减去第一部分的数量
	secondPart := total - firstPart
	return firstPart, secondPart
}

func TestRandoms(t *testing.T) {
	// 使用当前时间作为随机数种子，确保每次程序运行产生的随机数序列都不同
	rand.Seed(time.Now().UnixNano())

	/*randomNum := randInt(1, 100)
	fmt.Println("Random number:", randomNum)*/
	total := 10 // 假设总数是5
	parts := splitRandomParts(total)
	fmt.Println("分配结果:", parts)
}

// splitIntoRandomParts 将总数total随机分成多个部分，并返回这些部分的切片
func splitRandomParts(total int) []int {
	if total < 2 {
		// 如果总数小于2，则无法分成多个部分
		return []int{total}
	}

	// 创建一个切片来保存每个部分的数量
	var parts []int

	// 剩余要分配的副本数
	remaining := total

	// 随机决定要分成的部分数量（至少2个部分）
	numParts := rand.Intn(total-1) + 2

	// 确保每个部分至少获得1个副本
	for i := 0; i < numParts-1; i++ {
		// 生成一个随机数（1到剩余副本数之间）
		// 为了避免最后一个部分太小，我们可能需要调整随机数范围
		minPartSize := 1
		if remaining <= numParts-i {
			// 如果剩余副本数不足以让每个部分都至少获得1个，则调整最小部分大小
			minPartSize = remaining / (numParts - i)
			if remaining%(numParts-i) > 0 {
				minPartSize++
			}
		}
		// 生成一个大于等于minPartSize且小于等于remaining的随机数
		partSize := minPartSize + rand.Intn(remaining-minPartSize+1)
		parts = append(parts, partSize)
		remaining -= partSize
	}

	// 最后一个部分的数量就是剩余的副本数
	parts = append(parts, remaining)

	return parts
}

func TestNumRandom(t *testing.T) {
	total := 10   // 假设副本数是10
	numParts := 2 // 假设要分成5个集群

	parts, err := splitIntoParts(total, numParts)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println("分配结果:", parts)
}

// splitIntoParts 将总数total随机分成numParts个部分，并返回这些部分的切片
func splitIntoParts(total int, numParts int) ([]int, error) {
	if total < 1 || numParts < 1 {
		// 总数或部分数量不能小于1
		return nil, fmt.Errorf("total and numParts must be greater than 0")
	}
	if numParts > total {
		// 部分数量不能大于总数
		return nil, fmt.Errorf("numParts cannot be greater than total")
	}

	// 创建一个切片来保存每个部分的数量
	parts := make([]int, numParts)

	// 首先将每个部分都分配至少一个副本
	for i := range parts {
		parts[i] = 1
		total--
	}

	// 剩余要分配的副本数
	remaining := total

	// 随机分配剩余的副本
	for remaining > 0 {
		// 随机选择一个部分（索引从0到numParts-1）
		partIndex := rand.Intn(numParts)

		// 如果该部分加上一个副本后不会超过总数，则分配一个副本
		if parts[partIndex]+1 <= total {
			parts[partIndex]++
			remaining--
		}
	}

	return parts, nil
}
