package gkeheterogeneousstatus

import "testing"

func TestPopulateNodeUsagesCountsPodsAndDeduplicatesClusterNodes(t *testing.T) {
	components := []ComponentStatus{
		{
			ComponentType: "tikv",
			Pods: []PodPlacement{
				{
					Node:               "node-a",
					NodePool:           "pool-a",
					ActualInstanceType: "n2-standard-8",
					ExpectedRole:       "base",
					PlacementStatus:    PlacementStatusBase,
				},
				{
					Node:               "node-a",
					NodePool:           "pool-a",
					ActualInstanceType: "n2-standard-8",
					ExpectedRole:       "base",
					PlacementStatus:    PlacementStatusBase,
				},
			},
		},
	}

	out, clusterUsages := PopulateNodeUsages(components)

	if len(out) != 1 || len(out[0].NodeUsages) != 1 {
		t.Fatalf("component node usages = %+v, want one placement group", out)
	}
	if usage := out[0].NodeUsages[0]; usage.UsedNodeCount != 1 || usage.PodCount != 2 {
		t.Fatalf("component usage = %+v, want one node and two pods", usage)
	}
	if out[0].NodeSummary == nil ||
		out[0].NodeSummary.TotalUsedNodeCount != 1 ||
		out[0].NodeSummary.BaseUsedNodeCount != 1 {
		t.Fatalf("component node summary = %+v, want one base used node", out[0].NodeSummary)
	}
	if len(clusterUsages) != 1 {
		t.Fatalf("cluster node usages = %+v, want one placement group", clusterUsages)
	}
	if usage := clusterUsages[0]; usage.NodePool != "pool-a" || usage.UsedNodeCount != 1 || usage.PodCount != 2 {
		t.Fatalf("cluster usage = %+v, want one node and two pods", usage)
	}
}

func TestNodeSummaryFromComponentsDeduplicatesAndRanksNodes(t *testing.T) {
	components := []ComponentStatus{
		{
			ComponentType: "tidb",
			Pods: []PodPlacement{
				{
					Node:            "node-a",
					PlacementStatus: PlacementStatusBase,
				},
				{
					Node:            "node-b",
					PlacementStatus: PlacementStatusMismatch,
				},
				{
					Node:            "node-c",
					PlacementStatus: PlacementStatusUnknown,
				},
				{
					Node:            "node-d",
					PlacementStatus: PlacementStatusBase,
				},
			},
		},
		{
			ComponentType: "tikv",
			Pods: []PodPlacement{
				{
					Node:            "node-a",
					PlacementStatus: PlacementStatusHeterogeneous,
				},
				{
					Node:            "node-b",
					PlacementStatus: PlacementStatusBase,
				},
			},
		},
	}

	summary := NodeSummaryFromComponents(components)

	if summary == nil {
		t.Fatal("node summary = nil, want summary")
	}
	if summary.TotalUsedNodeCount != 4 ||
		summary.HeterogeneousUsedNodeCount != 1 ||
		summary.BaseUsedNodeCount != 1 ||
		summary.UnknownUsedNodeCount != 1 ||
		summary.MismatchUsedNodeCount != 1 {
		t.Fatalf("node summary = %+v, want heterogeneous/base/unknown counts", summary)
	}
	if summary.HeterogeneousUsedNodeRatio == nil || *summary.HeterogeneousUsedNodeRatio != float64(1)/float64(4) {
		t.Fatalf("heterogeneous ratio = %v, want 1/4", summary.HeterogeneousUsedNodeRatio)
	}
}

func TestNodeSummaryFromComponentsCountsPlacementStatusOnly(t *testing.T) {
	components := []ComponentStatus{
		{
			ComponentType: "tikv",
			Pods: []PodPlacement{
				{
					Node:               "node-extra",
					ActualInstanceType: "c3-standard-8",
					PlacementStatus:    PlacementStatusHeterogeneous,
				},
				{
					Node:               "node-mismatch",
					ActualInstanceType: "c3-standard-8",
					PlacementStatus:    PlacementStatusMismatch,
				},
				{
					Node:               "node-unknown",
					ActualInstanceType: "c3-standard-8",
					PlacementStatus:    PlacementStatusUnknown,
				},
				{
					Node:               "node-base",
					ActualInstanceType: "n2-standard-8",
					PlacementStatus:    PlacementStatusBase,
				},
			},
		},
	}

	summary := NodeSummaryFromComponents(components)

	if summary == nil {
		t.Fatal("node summary = nil, want summary")
	}
	if summary.TotalUsedNodeCount != 4 ||
		summary.HeterogeneousUsedNodeCount != 1 ||
		summary.MismatchUsedNodeCount != 1 ||
		summary.UnknownUsedNodeCount != 1 ||
		summary.BaseUsedNodeCount != 1 {
		t.Fatalf("node summary = %+v, want placement-status counts only", summary)
	}
	if summary.HeterogeneousUsedNodeRatio == nil ||
		*summary.HeterogeneousUsedNodeRatio != float64(1)/float64(4) {
		t.Fatalf("heterogeneous ratio = %v, want 1/4", summary.HeterogeneousUsedNodeRatio)
	}
}

func TestPodGroupsFromPlacementsGroupsAndSortsPodNames(t *testing.T) {
	groups := PodGroupsFromPlacements([]PodPlacement{
		{
			Pod:                  "tikv-1",
			NodePool:             "pool-extra",
			ActualInstanceType:   "n2d-standard-8",
			ActualComputeClass:   "tikv-priority",
			ExpectedRole:         "extra",
			ExpectedInstanceType: "n2d-standard-8",
			ExpectedComputeClass: "tikv-priority",
			PlacementStatus:      PlacementStatusHeterogeneous,
		},
		{
			Pod:                  "tikv-0",
			NodePool:             "pool-extra",
			ActualInstanceType:   "n2d-standard-8",
			ActualComputeClass:   "tikv-priority",
			ExpectedRole:         "extra",
			ExpectedInstanceType: "n2d-standard-8",
			ExpectedComputeClass: "tikv-priority",
			PlacementStatus:      PlacementStatusHeterogeneous,
		},
		{
			Pod:             "tikv-2",
			PlacementStatus: PlacementStatusPending,
		},
	})

	if len(groups) != 2 {
		t.Fatalf("groups = %+v, want two groups", groups)
	}
	if groups[0].PlacementStatus != PlacementStatusPending || groups[0].PodCount != 1 || groups[0].Pods[0] != "tikv-2" {
		t.Fatalf("first group = %+v, want pending group first", groups[0])
	}
	if groups[1].PlacementStatus != PlacementStatusHeterogeneous || groups[1].PodCount != 2 {
		t.Fatalf("second group = %+v, want heterogeneous group with two pods", groups[1])
	}
	if got := groups[1].Pods; len(got) != 2 || got[0] != "tikv-0" || got[1] != "tikv-1" {
		t.Fatalf("heterogeneous pods = %+v, want sorted pod names", got)
	}
}

func TestCompactSnapshotBuildsPodGroupsAndClearsFullPlacements(t *testing.T) {
	snapshot := Snapshot{
		Components: []ComponentStatus{
			{
				ComponentType: "tikv",
				PodGroups: []PodGroup{
					{
						PlacementStatus: PlacementStatusPending,
						PodCount:        99,
						Pods:            []string{"stale"},
					},
				},
				Pods: []PodPlacement{
					{
						Pod:             "tikv-0",
						PlacementStatus: PlacementStatusBase,
						NodePool:        "pool-base",
					},
				},
			},
		},
	}

	compacted := CompactSnapshot(snapshot)

	if len(compacted.Components[0].Pods) != 0 {
		t.Fatalf("full placements = %+v, want cleared", compacted.Components[0].Pods)
	}
	groups := compacted.Components[0].PodGroups
	if len(groups) != 1 || groups[0].PodCount != 1 || groups[0].Pods[0] != "tikv-0" {
		t.Fatalf("pod groups = %+v, want compacted pod group", groups)
	}
}
