package hangman

import (
	"bufio"
	_ "embed"
	"errors"
	"math/rand/v2"
	"strings"
	"sync"
)

// The bank. gogobee kept its phrases in a file it read at boot out of a path in
// an env var, which meant the game was one missing file away from not existing.
// Embedding it means the casino cannot start without its phrases, which is the
// correct relationship between a game and the thing it is about.
//
//go:embed phrases.txt
var phrasesTxt string

// ErrNoPhrases means the bank has nothing at this length. It can only happen if
// someone edits phrases.txt down past a tier, and it is a programming error
// rather than anything a player did — but it's an error, not a panic, because a
// casino that won't boot is worse than one game being shut.
var ErrNoPhrases = errors.New("hangman: no phrases in that tier")

var (
	shelvesOnce sync.Once
	shelves     map[string][]string // tier slug -> the phrases that fit it
)

// load sorts the bank onto one shelf per tier, once. Comments and blank lines
// are dropped, and so is anything too short to be a game — the tiers' own Min
// is the floor.
func load() {
	shelves = make(map[string][]string, len(Tiers))
	sc := bufio.NewScanner(strings.NewReader(phrasesTxt))
	for sc.Scan() {
		line := strings.TrimSpace(sc.Text())
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		n := len([]rune(line))
		for _, t := range Tiers {
			if n >= t.Min && n <= t.Max {
				shelves[t.Slug] = append(shelves[t.Slug], line)
				break
			}
		}
	}
}

// Shelf is how many phrases a tier has. Exists so a test can assert the bank
// hasn't been edited out from under a tier.
func Shelf(slug string) int {
	shelvesOnce.Do(load)
	return len(shelves[slug])
}

// drawPhrase picks one phrase from a tier's shelf. The rng is threaded, never
// the package global, so a game replays exactly from the seed in its audit row.
func drawPhrase(t Tier, rng *rand.Rand) (string, error) {
	shelvesOnce.Do(load)
	shelf := shelves[t.Slug]
	if len(shelf) == 0 {
		return "", ErrNoPhrases
	}
	return shelf[rng.IntN(len(shelf))], nil
}
