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

package predicates_test

import (
	"testing"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"sigs.k8s.io/controller-runtime/pkg/event"

	"github.com/projectcapsule/capsule/pkg/runtime/predicates"
)

func TestLabelsMatchingPredicate_Matches(t *testing.T) {
	t.Parallel()

	mk := func(lbl map[string]string) *unstructured.Unstructured {
		u := &unstructured.Unstructured{}
		u.SetAPIVersion("v1")
		u.SetKind("ConfigMap")
		u.SetName("cm")
		u.SetNamespace("ns")
		u.SetLabels(lbl)
		return u
	}

	t.Run("empty match map matches everything (including nil labels)", func(t *testing.T) {
		t.Parallel()

		p := predicates.LabelsMatchingPredicate{Match: map[string]string{}}

		if !p.Create(event.CreateEvent{Object: mk(nil)}) {
			t.Fatalf("Create should match when Match is empty")
		}
		if !p.Update(event.UpdateEvent{ObjectNew: mk(nil)}) {
			t.Fatalf("Update should match when Match is empty")
		}
		if !p.Delete(event.DeleteEvent{Object: mk(nil)}) {
			t.Fatalf("Delete should match when Match is empty")
		}
		if !p.Generic(event.GenericEvent{Object: mk(nil)}) {
			t.Fatalf("Generic should match when Match is empty")
		}
	})

	t.Run("non-empty match requires all key/value pairs", func(t *testing.T) {
		t.Parallel()

		p := predicates.LabelsMatchingPredicate{Match: map[string]string{"app": "x", "tier": "backend"}}

		// Missing labels
		if p.Create(event.CreateEvent{Object: mk(nil)}) {
			t.Fatalf("expected no match when labels are nil")
		}

		// Partial match
		if p.Create(event.CreateEvent{Object: mk(map[string]string{"app": "x"})}) {
			t.Fatalf("expected no match when one label missing")
		}

		// Wrong value
		if p.Create(event.CreateEvent{Object: mk(map[string]string{"app": "x", "tier": "frontend"})}) {
			t.Fatalf("expected no match when value differs")
		}

		// Full match
		if !p.Create(event.CreateEvent{Object: mk(map[string]string{"app": "x", "tier": "backend"})}) {
			t.Fatalf("expected match when all labels match")
		}
	})

	t.Run("Update checks new object only", func(t *testing.T) {
		t.Parallel()

		p := predicates.LabelsMatchingPredicate{Match: map[string]string{"app": "x"}}

		// Old matches, new doesn't => false
		if p.Update(event.UpdateEvent{ObjectOld: mk(map[string]string{"app": "x"}), ObjectNew: mk(map[string]string{"app": "y"})}) {
			t.Fatalf("expected false when new object does not match")
		}

		// Old doesn't match, new matches => true
		if !p.Update(event.UpdateEvent{ObjectOld: mk(map[string]string{"app": "y"}), ObjectNew: mk(map[string]string{"app": "x"})}) {
			t.Fatalf("expected true when new object matches")
		}
	})
}
