package entity

import (
	"testing"

	"github.com/stretchr/testify/require"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/clusterevent"
)

func TestBuildClusterTopologyIncludesStorageFacts(t *testing.T) {
	dataVolumeType := globaldedicatedv1.StorageMu_GP3
	raftLogVolumeType := globaldedicatedv1.StorageMu_IO2
	cluster := &Cluster{
		Cluster: &globaldedicatedv1.Cluster{
			ClusterComponents: []*globaldedicatedv1.Cluster_ClusterComponent{
				{
					ComponentType:  globaldedicatedv1.ComponentType_TIKV,
					SpuName:        "8C16G",
					SpuDisplayName: "8 vCPU, 16 GiB",
					SpuKey:         "tikv-spu",
					SkuKey:         "tikv-sku",
					Count:          uint32Ptr(3),
					Storages: map[string]*globaldedicatedv1.Cluster_ClusterComponent_Storage{
						globaldedicatedv1.Sku_DATA.String(): {
							DiskType:        globaldedicatedv1.Sku_DATA,
							SizeGi:          512,
							Iops:            9000,
							ThroughputMibps: 350,
							VolumeType:      &dataVolumeType,
							AdditionalIops:  uint32Ptr(5000),
						},
						globaldedicatedv1.Sku_RAFT_LOG.String(): {
							DiskType:        globaldedicatedv1.Sku_RAFT_LOG,
							Iops:            12000,
							ThroughputMibps: 500,
							VolumeType:      &raftLogVolumeType,
						},
					},
				},
			},
		},
	}

	topology := BuildClusterTopology(cluster, 0)

	tikv := topology.Components[globaldedicatedv1.ComponentType_TIKV.String()]
	require.NotNil(t, tikv)
	require.Equal(t, uint32(512), tikv.StorageSizeGi)
	require.Equal(t, uint32(9000), tikv.StorageIops)
	require.Equal(t, uint32(350), tikv.StorageThroughput)
	require.Equal(t, uint32(5000), tikv.AdditionalIops)
	require.Equal(t, globaldedicatedv1.StorageMu_GP3.String(), tikv.VolumeType)
	require.Equal(t, uint32(12000), tikv.RaftLogIops)
	require.Equal(t, uint32(500), tikv.RaftLogThroughput)
	require.Equal(t, globaldedicatedv1.StorageMu_IO2.String(), tikv.RaftLogVolumeType)
}

func TestBuildScaleOperationContext(t *testing.T) {
	dataVolumeType := globaldedicatedv1.StorageMu_GP3
	operationContext := BuildScaleOperationContext(
		string(clusterevent.OperationSourceModifyClusterVolume),
		[]string{"component_type", "disk_type", "storage_iops"},
		[]*globaldedicatedv1.Cluster_ClusterComponent{
			{
				ComponentType: globaldedicatedv1.ComponentType_TIKV,
				Storages: map[string]*globaldedicatedv1.Cluster_ClusterComponent_Storage{
					globaldedicatedv1.Sku_DATA.String(): {
						DiskType:   globaldedicatedv1.Sku_DATA,
						Iops:       9000,
						VolumeType: &dataVolumeType,
					},
				},
			},
		},
	)

	require.Equal(t, string(clusterevent.OperationSourceModifyClusterVolume), operationContext.SourceOperation)
	require.Equal(t, []string{"component_type", "disk_type", "storage_iops"}, operationContext.RequestedFields)
	require.Len(t, operationContext.RequestedComponents, 1)
	require.Equal(t, globaldedicatedv1.ComponentType_TIKV.String(), operationContext.RequestedComponents[0].ComponentType)
	require.Equal(
		t,
		globaldedicatedv1.Sku_DATA.String(),
		operationContext.RequestedComponents[0].Storages[globaldedicatedv1.Sku_DATA.String()].DiskType,
	)
	require.Equal(t, uint32(9000), operationContext.RequestedComponents[0].Storages[globaldedicatedv1.Sku_DATA.String()].Iops)
	require.Equal(
		t,
		globaldedicatedv1.StorageMu_GP3.String(),
		operationContext.RequestedComponents[0].Storages[globaldedicatedv1.Sku_DATA.String()].VolumeType,
	)
}

func TestBuildApplyOperationContext(t *testing.T) {
	operationContext := BuildApplyOperationContext(
		&globaldedicatedv1.ClusterApplyMask{
			ComponentApplyMasks: []*globaldedicatedv1.ClusterApplyMask_ComponentApplyMask{
				{
					ComponentType: globaldedicatedv1.ComponentType_TIKV,
					Configuration: true,
				},
			},
			Strategy: globaldedicatedv1.ClusterApplyMask_HOT_RELOAD,
		},
		map[string][]string{
			"tikv": {"HOT_RELOAD_change_config"},
		},
	)

	require.Equal(t, string(clusterevent.OperationSourceApplyCluster), operationContext.SourceOperation)
	require.Equal(t, []string{"tikv.configuration"}, operationContext.RequestedFields)
	require.Equal(t, []string{"HOT_RELOAD_change_config"}, operationContext.ComponentOperations["tikv"])
	require.Equal(t, globaldedicatedv1.ClusterApplyMask_HOT_RELOAD.String(), operationContext.ApplyMask.Strategy)
	require.Len(t, operationContext.ApplyMask.ComponentApplyMasks, 1)
	require.True(t, operationContext.ApplyMask.ComponentApplyMasks[0].Configuration)
}

func uint32Ptr(v uint32) *uint32 {
	return &v
}
