/**
 * Idle-preferring worker selection helper.
 *
 * Workers run as in-process instances that each poll the DB for jobs. Without
 * coordination, whichever worker's poll timer fires first claims the next job —
 * so a busy worker can grab work while an idle sibling sits at 0/n. This helper
 * implements the "most-free-wins" rule: before claiming, a worker checks its
 * siblings and yields to one that has STRICTLY more free slots (scoped to
 * siblings that are available and actually serve the job's role). A 0/n idle
 * worker therefore always beats a partially-loaded one.
 *
 * Returns the index of the idlest qualifying competitor, or -1 when the caller
 * is already (tied for) the most free and should claim the job itself.
 */
export interface ClaimCandidate {
  /** max_concurrency − inflight for this candidate. */
  freeSlots: number;
  /** Running, healthy, enabled — would actually claim if poked. */
  availableForClaim: boolean;
  /** Serves the role of the job about to be claimed. */
  servesRole: boolean;
}

export function pickIdlerIndex(
  selfFreeSlots: number,
  candidates: readonly ClaimCandidate[],
): number {
  let bestIdx = -1;
  let bestFree = selfFreeSlots; // a competitor must STRICTLY exceed this to win
  for (let i = 0; i < candidates.length; i++) {
    const c = candidates[i];
    if (!c.availableForClaim || !c.servesRole) continue;
    if (c.freeSlots > bestFree) {
      bestIdx = i;
      bestFree = c.freeSlots;
    }
  }
  return bestIdx;
}
