package membership

import (
	"sync"
	"testing"
)

// TestInflightLimiterBasics covers the limiter contract: granting within the cap
// (golden), the exact boundary (edge), refusal over the cap without mutating the
// counter (error), the disabled mode, and the defensive clamp on over-release.
func TestInflightLimiterBasics(t *testing.T) {
	l := newInflightLimiter(100)

	// Golden: a reservation within the cap is granted and reflected.
	if !l.tryAcquire(60) {
		t.Fatalf("acquire 60 within cap 100 should grant")
	}
	if l.inFlight() != 60 {
		t.Fatalf("inFlight = %d, want 60", l.inFlight())
	}

	// Edge: exactly reaching the cap (60+40 == 100) is granted.
	if !l.tryAcquire(40) {
		t.Fatalf("acquire to the exact cap should grant")
	}
	if l.inFlight() != 100 {
		t.Fatalf("inFlight = %d, want 100", l.inFlight())
	}

	// Error: one more byte over the full cap is refused, and the counter is left
	// untouched (a refused reservation reserves nothing).
	if l.tryAcquire(1) {
		t.Fatalf("acquire over a full cap must be refused")
	}
	if l.inFlight() != 100 {
		t.Fatalf("a refused acquire must not change inFlight; got %d", l.inFlight())
	}

	// Release frees capacity again.
	l.release(100)
	if l.inFlight() != 0 {
		t.Fatalf("inFlight after full release = %d, want 0", l.inFlight())
	}

	// Defensive: an over-release never drives the counter negative.
	l.release(50)
	if l.inFlight() != 0 {
		t.Fatalf("over-release must clamp at 0; got %d", l.inFlight())
	}
}

// TestInflightLimiterDisabled verifies that a non-positive cap disables the
// limiter: every reservation is granted and nothing is tracked (the loopback/dev
// posture).
func TestInflightLimiterDisabled(t *testing.T) {
	for _, max := range []int64{0, -1} {
		l := newInflightLimiter(max)
		if !l.tryAcquire(1 << 30) {
			t.Fatalf("disabled limiter (max=%d) must always grant", max)
		}
		if l.inFlight() != 0 {
			t.Fatalf("disabled limiter must not track usage; got %d", l.inFlight())
		}
		l.release(1 << 30) // no-op, must not panic
	}
}

// TestInflightLimiterConcurrent hammers the limiter from many goroutines with
// equal-sized acquire/release pairs and asserts the invariant never breaks: the
// counter returns to 0 and never exceeds the cap. Run with -race for the memory
// model guarantee.
func TestInflightLimiterConcurrent(t *testing.T) {
	const cap = 1000
	const chunk = 7
	l := newInflightLimiter(cap)

	var wg sync.WaitGroup
	for g := 0; g < 64; g++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for i := 0; i < 2000; i++ {
				if l.tryAcquire(chunk) {
					if f := l.inFlight(); f > cap {
						t.Errorf("inFlight %d exceeded cap %d", f, cap)
						return
					}
					l.release(chunk)
				}
			}
		}()
	}
	wg.Wait()
	if l.inFlight() != 0 {
		t.Fatalf("after all goroutines, inFlight = %d, want 0", l.inFlight())
	}
}
