/**
 * Auto-name generator for workleaves.
 *
 * Produces names in the form `<adjective>-<tree>` (e.g. "sunny-baobab")
 * where both words are short, single-word slugs that are globally
 * recognizable. The tree list is intentionally balanced across
 * continents so the namespace doesn't lean toward any one region.
 *
 * Picks are randomized, then checked for collisions against a caller-
 * supplied "in use" predicate (typically: existing workleaf slugs +
 * existing git branches). If we exhaust the random search, we fall
 * back to a deterministic sweep so we never give up on a free name
 * unless the entire 20×20 namespace is taken.
 */

export const ADJECTIVES = Object.freeze([
  'bright',
  'calm',
  'cheerful',
  'cozy',
  'gentle',
  'golden',
  'happy',
  'kind',
  'lively',
  'lucky',
  'merry',
  'mighty',
  'peaceful',
  'quiet',
  'snug',
  'steady',
  'sunny',
  'sweet',
  'warm',
  'wise',
])

// 20 trees, ~2-3 per continent — globally recognizable, single-word
// (hyphenated only if unavoidable). No region dominates the namespace.
export const TREES = Object.freeze([
  // Africa
  'baobab',
  'acacia',
  'marula',
  // South America
  'jacaranda',
  'ceiba',
  'mahogany',
  // Asia
  'bamboo',
  'ginkgo',
  'banyan',
  'sakura',
  // Oceania / Pacific
  'eucalyptus',
  'kauri',
  // Mediterranean / West Asia / North Africa
  'olive',
  'cedar',
  'cypress',
  // North America
  'redwood',
  'maple',
  // Europe
  'oak',
  'birch',
  // Pantropical
  'palm',
])

/**
 * Total size of the name namespace (adjectives × trees).
 */
export const NAMESPACE_SIZE = ADJECTIVES.length * TREES.length

/**
 * Pick a random integer in [0, max).
 */
function randInt(max, random = Math.random) {
  return Math.floor(random() * max)
}

/**
 * Compose a name from indices.
 */
function compose(adjIdx, treeIdx) {
  return `${ADJECTIVES[adjIdx]}-${TREES[treeIdx]}`
}

/**
 * Generate a unique workleaf name not already taken.
 *
 * @param {(name: string) => boolean} isTaken
 *        Returns true if a name is already in use. Called for each
 *        candidate the generator considers.
 * @param {object} [opts]
 * @param {() => number} [opts.random=Math.random]
 *        Override the random source (for tests).
 * @param {number} [opts.maxRandomAttempts=50]
 *        How many random picks to try before falling back to a sweep.
 * @returns {string|null} A free name, or null if the entire namespace
 *          is exhausted.
 */
export function generateWorkleafName(isTaken, opts = {}) {
  const random = opts.random || Math.random
  const maxRandomAttempts = opts.maxRandomAttempts ?? 50
  const check = typeof isTaken === 'function' ? isTaken : () => false

  for (let i = 0; i < maxRandomAttempts; i++) {
    const candidate = compose(
      randInt(ADJECTIVES.length, random),
      randInt(TREES.length, random),
    )
    if (!check(candidate)) return candidate
  }

  // Random search exhausted — deterministic sweep so we never miss a
  // free name unless the whole namespace is genuinely full.
  const adjStart = randInt(ADJECTIVES.length, random)
  const treeStart = randInt(TREES.length, random)
  for (let a = 0; a < ADJECTIVES.length; a++) {
    for (let t = 0; t < TREES.length; t++) {
      const candidate = compose(
        (adjStart + a) % ADJECTIVES.length,
        (treeStart + t) % TREES.length,
      )
      if (!check(candidate)) return candidate
    }
  }

  return null
}
