package slot

import (
	"sync"
	"testing"
)

const testFirstID = 2

func TestAllocatorReturnsLowestFreeID(t *testing.T) {
	allocator := NewAllocator(testFirstID, 3)

	first, err := allocator.Acquire()
	if err != nil || first != testFirstID {
		t.Fatalf("first Acquire() = (%d, %v), want (%d, nil)", first, err, testFirstID)
	}
	second, err := allocator.Acquire()
	if err != nil || second != testFirstID+1 {
		t.Fatalf("second Acquire() = (%d, %v), want (%d, nil)", second, err, testFirstID+1)
	}
	if err := allocator.Release(first); err != nil {
		t.Fatalf("Release(%d): %v", first, err)
	}
	reused, err := allocator.Acquire()
	if err != nil || reused != first {
		t.Fatalf("reused Acquire() = (%d, %v), want (%d, nil)", reused, err, first)
	}
}

func TestAllocatorConcurrentAcquireIsUnique(t *testing.T) {
	const capacity = 128
	allocator := NewAllocator(testFirstID, capacity)
	ids := make(chan int, capacity)
	errs := make(chan error, capacity)
	var wg sync.WaitGroup
	for range capacity {
		wg.Add(1)
		go func() {
			defer wg.Done()
			id, err := allocator.Acquire()
			if err != nil {
				errs <- err
				return
			}
			ids <- id
		}()
	}
	wg.Wait()
	close(ids)
	close(errs)
	for err := range errs {
		t.Fatalf("concurrent Acquire(): %v", err)
	}

	seen := make(map[int]struct{}, capacity)
	for id := range ids {
		if _, ok := seen[id]; ok {
			t.Fatalf("slot ID %d was acquired twice", id)
		}
		seen[id] = struct{}{}
	}
	if len(seen) != capacity {
		t.Fatalf("unique acquired IDs = %d, want %d", len(seen), capacity)
	}
}