// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0

package utils_test

import (
	"testing"

	capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
	"github.com/projectcapsule/capsule/pkg/utils"
)

func TestBuildNodeSelector_NilAnnotationsInitializesMap(t *testing.T) {
	tnt := &capsulev1beta2.Tenant{}
	tnt.Spec.NodeSelector = map[string]string{
		"disktype": "ssd",
	}

	out := utils.BuildNodeSelector(tnt, nil)

	if out == nil {
		t.Fatalf("expected non-nil map")
	}

	got, ok := out[utils.NodeSelectorAnnotation]
	if !ok {
		t.Fatalf("expected %q annotation to be set", utils.NodeSelectorAnnotation)
	}
	if got != "disktype=ssd" {
		t.Fatalf("unexpected annotation value: got %q want %q", got, "disktype=ssd")
	}
}

func TestBuildNodeSelector_ExistingAnnotationsPreserved(t *testing.T) {
	tnt := &capsulev1beta2.Tenant{}
	tnt.Spec.NodeSelector = map[string]string{
		"disktype": "ssd",
	}

	in := map[string]string{
		"keep": "me",
	}

	out := utils.BuildNodeSelector(tnt, in)

	if out["keep"] != "me" {
		t.Fatalf("expected existing annotation key to be preserved")
	}
	if out[utils.NodeSelectorAnnotation] != "disktype=ssd" {
		t.Fatalf("unexpected node selector annotation: got %q", out[utils.NodeSelectorAnnotation])
	}
}

func TestBuildNodeSelector_SortsSelectorsDeterministically(t *testing.T) {
	tnt := &capsulev1beta2.Tenant{}
	// Intentionally unordered map
	tnt.Spec.NodeSelector = map[string]string{
		"b": "2",
		"a": "1",
		"c": "3",
	}

	out := utils.BuildNodeSelector(tnt, map[string]string{})

	got := out[utils.NodeSelectorAnnotation]
	want := "a=1,b=2,c=3"
	if got != want {
		t.Fatalf("expected deterministic sorted annotation: got %q want %q", got, want)
	}
}

func TestBuildNodeSelector_EmptyNodeSelectorSetsEmptyAnnotation(t *testing.T) {
	tnt := &capsulev1beta2.Tenant{}
	tnt.Spec.NodeSelector = map[string]string{}

	out := utils.BuildNodeSelector(tnt, map[string]string{})

	got, ok := out[utils.NodeSelectorAnnotation]
	if !ok {
		t.Fatalf("expected %q annotation to be present even for empty selector", utils.NodeSelectorAnnotation)
	}
	if got != "" {
		t.Fatalf("expected empty annotation value, got %q", got)
	}
}

func TestBuildNodeSelector_NilNodeSelectorSetsEmptyAnnotation(t *testing.T) {
	tnt := &capsulev1beta2.Tenant{}
	tnt.Spec.NodeSelector = nil

	out := utils.BuildNodeSelector(tnt, map[string]string{})

	got, ok := out[utils.NodeSelectorAnnotation]
	if !ok {
		t.Fatalf("expected %q annotation to be present even for nil selector", utils.NodeSelectorAnnotation)
	}
	if got != "" {
		t.Fatalf("expected empty annotation value, got %q", got)
	}
}

func TestBuildNodeSelector_ReturnsSameMapInstance(t *testing.T) {
	tnt := &capsulev1beta2.Tenant{}
	tnt.Spec.NodeSelector = map[string]string{"a": "1"}

	in := map[string]string{"x": "y"}
	out := utils.BuildNodeSelector(tnt, in)

	// BuildNodeSelector mutates and returns the same map reference
	if &in == &out {
		// Note: maps are reference types; direct pointer comparison isn't meaningful.
	}
	if len(out) != 2 {
		t.Fatalf("expected 2 keys in resulting map, got %d", len(out))
	}
	if out["x"] != "y" {
		t.Fatalf("expected original key to remain")
	}
}
