package main

import (
	"sync"
	"testing"
	"time"
)

// The award snapshot must be built ONCE however many callers ask at once.
//
// The cache lock is released before the logbook is read, so every caller that
// arrives during a build used to miss the cache and start its own. Opening the
// Awards panel does exactly that: a field log showed three pulls of the same
// 123 615 QSOs inside ten seconds, and three copies alive together took the Go
// heap from 725 MB to 2.5 GB.
//
// This models the same shape — a cheap cache check, a slow build, a shared
// result — against the pattern awardSnapshot now uses, so the invariant is
// pinned without needing a logbook.
func TestSnapshotBuildsOncePerRevision(t *testing.T) {
	var (
		cacheMu sync.Mutex
		buildMu sync.Mutex
		cached  []int
		rev     = "r1"
		gotRev  string
		builds  int
	)

	get := func() []int {
		cacheMu.Lock()
		if cached != nil && gotRev == rev {
			defer cacheMu.Unlock()
			return cached
		}
		cacheMu.Unlock()

		buildMu.Lock()
		defer buildMu.Unlock()
		// Re-check: whoever held the build lock has just finished.
		cacheMu.Lock()
		if cached != nil && gotRev == rev {
			defer cacheMu.Unlock()
			return cached
		}
		cacheMu.Unlock()

		time.Sleep(50 * time.Millisecond) // the logbook read
		out := []int{1, 2, 3}

		cacheMu.Lock()
		builds++
		cached, gotRev = out, rev
		cacheMu.Unlock()
		return out
	}

	var wg sync.WaitGroup
	results := make([][]int, 8)
	for i := range results {
		wg.Add(1)
		go func(i int) { defer wg.Done(); results[i] = get() }(i)
	}
	wg.Wait()

	if builds != 1 {
		t.Errorf("%d builds for one revision — each concurrent caller pulled the whole logbook again", builds)
	}
	for i, r := range results {
		if len(r) != 3 {
			t.Errorf("caller %d got %v", i, r)
		}
	}

	// A new revision must rebuild: the guard is against duplicate work, not
	// against a logbook that changed.
	cacheMu.Lock()
	rev = "r2"
	cacheMu.Unlock()
	get()
	if builds != 2 {
		t.Errorf("builds = %d after the revision moved, want 2 — a changed logbook must be re-read", builds)
	}
}
