package nodegroup

import (
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestBuildPrimaryLabels(t *testing.T) {
	spec := InstanceTypeSpec{
		Spec:              "8C16G",
		InstanceType:      "m5.2xlarge",
		AvailableCPU:      "7",
		AvailableMemoryMi: "15360",
	}

	labels := BuildPrimaryLabels(&spec)

	assert.Equal(t, "8C16G", labels[LabelNodeSpec])
	assert.Equal(t, strconv.Itoa(PriorityPrimary), labels[LabelPriority])
	assert.Equal(t, ManagedByInstanceTypeManager, labels[LabelManagedBy])
}

func TestBuildFallbackLabels(t *testing.T) {
	spec := InstanceTypeSpec{
		Spec:              "8C16G",
		InstanceType:      "m5a.2xlarge",
		AvailableCPU:      "7",
		AvailableMemoryMi: "15360",
	}
	primarySpec := "8C16G"

	labels := BuildFallbackLabels(&spec, primarySpec, PriorityFallback)

	assert.Equal(t, "8C16G", labels[LabelNodeSpec])
	assert.Equal(t, strconv.Itoa(PriorityFallback), labels[LabelPriority])
	assert.Equal(t, ManagedByInstanceTypeManager, labels[LabelManagedBy])
	assert.Equal(t, "8C16G", labels[LabelRealNodeSpec])
	assert.NotEmpty(t, labels[LabelShortageDetectedAt])
}

func TestAddDeprecationLabels(t *testing.T) {
	existingLabels := map[string]string{
		LabelNodeSpec: "8C16G",
	}

	result := AddDeprecationLabels(existingLabels, "hard")

	assert.Equal(t, "8C16G", result[LabelNodeSpec])
	assert.Equal(t, "hard", result[LabelDeprecationPhase])
	assert.NotEmpty(t, result[LabelDeprecatedAt])

	assert.NotEqual(t, existingLabels, result)
	_, hasPhase := existingLabels[LabelDeprecationPhase]
	assert.False(t, hasPhase)
}

func TestAddDeprecationLabels_NilInput(t *testing.T) {
	result := AddDeprecationLabels(nil, "soft")

	assert.Equal(t, "soft", result[LabelDeprecationPhase])
	assert.NotEmpty(t, result[LabelDeprecatedAt])
}

func TestMergeLabels(t *testing.T) {
	tests := []struct {
		name     string
		existing map[string]string
		new      map[string]string
		want     map[string]string
	}{
		{
			name:     "both nil",
			existing: nil,
			new:      nil,
			want:     map[string]string{},
		},
		{
			name:     "existing nil",
			existing: nil,
			new:      map[string]string{"key1": "val1"},
			want:     map[string]string{"key1": "val1"},
		},
		{
			name:     "new nil",
			existing: map[string]string{"key1": "val1"},
			new:      nil,
			want:     map[string]string{"key1": "val1"},
		},
		{
			name:     "no overlap",
			existing: map[string]string{"key1": "val1"},
			new:      map[string]string{"key2": "val2"},
			want:     map[string]string{"key1": "val1", "key2": "val2"},
		},
		{
			name:     "with overlap - new wins",
			existing: map[string]string{"key1": "oldval", "key2": "val2"},
			new:      map[string]string{"key1": "newval", "key3": "val3"},
			want:     map[string]string{"key1": "newval", "key2": "val2", "key3": "val3"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := MergeLabels(tt.existing, tt.new)
			assert.Equal(t, tt.want, result)

			if tt.existing != nil {
				for k, v := range tt.existing {
					if _, overridden := tt.new[k]; !overridden {
						assert.Equal(t, v, tt.existing[k])
					}
				}
			}
		})
	}
}

func TestHasLabel(t *testing.T) {
	tests := []struct {
		name   string
		labels map[string]string
		key    string
		value  string
		want   bool
	}{
		{
			name:   "nil labels",
			labels: nil,
			key:    "key",
			value:  "value",
			want:   false,
		},
		{
			name:   "key exists with matching value",
			labels: map[string]string{"key": "value"},
			key:    "key",
			value:  "value",
			want:   true,
		},
		{
			name:   "key exists with different value",
			labels: map[string]string{"key": "other"},
			key:    "key",
			value:  "value",
			want:   false,
		},
		{
			name:   "key does not exist",
			labels: map[string]string{"other": "value"},
			key:    "key",
			value:  "value",
			want:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := HasLabel(tt.labels, tt.key, tt.value)
			assert.Equal(t, tt.want, result)
		})
	}
}

func TestIsManagedByInstanceTypeManager(t *testing.T) {
	tests := []struct {
		name   string
		labels map[string]string
		want   bool
	}{
		{
			name:   "managed",
			labels: map[string]string{LabelManagedBy: ManagedByInstanceTypeManager},
			want:   true,
		},
		{
			name:   "not managed",
			labels: map[string]string{LabelManagedBy: "other"},
			want:   false,
		},
		{
			name:   "no label",
			labels: map[string]string{},
			want:   false,
		},
		{
			name:   "nil labels",
			labels: nil,
			want:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := IsManagedByInstanceTypeManager(tt.labels)
			assert.Equal(t, tt.want, result)
		})
	}
}

func TestIsDeprecated(t *testing.T) {
	tests := []struct {
		name   string
		labels map[string]string
		want   bool
	}{
		{
			name:   "deprecated with phase",
			labels: map[string]string{LabelDeprecationPhase: "hard"},
			want:   true,
		},
		{
			name:   "not deprecated",
			labels: map[string]string{LabelNodeSpec: "8C16G"},
			want:   false,
		},
		{
			name:   "empty labels",
			labels: map[string]string{},
			want:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := IsDeprecated(tt.labels)
			assert.Equal(t, tt.want, result)
		})
	}
}

func TestPriorityValues(t *testing.T) {
	assert.Positive(t, PriorityPrimary, "primary priority should be positive")
	assert.Positive(t, PriorityFallback, "fallback priority should be positive for valid K8s label")
	assert.Greater(t, PriorityPrimary, PriorityFallback, "primary should have higher priority than fallback")
}
