package tracker

import (
	"sync"
	"testing"

	"github.com/emersion/go-imap/v2"
)

func TestNew(t *testing.T) {
	tr := New()
	if tr == nil {
		t.Fatal("New() returned nil")
	}
	if tr.ids == nil {
		t.Fatal("New() initialized with nil map")
	}
}

func TestLifecycle(t *testing.T) {
	tr := New()
	uid := imap.UID(123)

	// 1. First acquire should succeed.
	if !tr.TryAcquire(uid) {
		t.Fatalf(
			"expected TryAcquire(%d) to return true, got false",
			uid,
		)
	}

	// 2. Second acquire on same UID should fail.
	if tr.TryAcquire(uid) {
		t.Fatalf(
			"expected TryAcquire(%d) to return false when already held, got true",
			uid,
		)
	}

	// 3. Different UID should succeed.
	otherUID := imap.UID(456)
	if !tr.TryAcquire(otherUID) {
		t.Fatalf(
			"expected TryAcquire(%d) to return true, got false",
			otherUID,
		)
	}

	// 4. Release first UID.
	tr.Release(uid)

	// 5. First UID should be acquirable again.
	if !tr.TryAcquire(uid) {
		t.Fatalf(
			"expected TryAcquire(%d) to return true after Release, got false",
			uid,
		)
	}
}

func TestConcurrency(t *testing.T) {
	tr := New()
	uid := imap.UID(1)
	routines := 16
	var wg sync.WaitGroup

	// Counter for successful acquisitions.
	successCount := 0
	var mu sync.Mutex

	for range routines {
		wg.Go(func() {
			if tr.TryAcquire(uid) {
				mu.Lock()
				successCount++
				mu.Unlock()
			}
		})
	}
	wg.Wait()

	if successCount != 1 {
		t.Errorf(
			"expected exactly 1 successful acquisition for concurrent access to same UID, got %d",
			successCount,
		)
	}

	// Verify we can release safely after concurrent hammer.
	tr.Release(uid)
	if !tr.TryAcquire(uid) {
		t.Error("failed to acquire UID after concurrent test release")
	}
}
