// Общий чанковый раннер для крупных операций над слотами (бронь и снятие). Бэк работает по
// одному слоту в транзакции, поэтому крупный набор бьём на части (см. BOOK_CHUNK) и шлём
// последовательно, сообщая прогресс. Выделено из TguSlotsTab, чтобы покрыть юнит-тестами и не
// дублировать цикл в performBook/performCancel (DRY).

import { chunk } from "./slotSelection";

/**
 * Прогоняет `fn` по чанкам `items` последовательно, после каждого чанка зовёт `onProgress`.
 * Возвращает по одному результату на чанк (порядок сохраняется): для брони это списки
 * забронированных слотов, для снятия — числа снятых. Сведение (flat/сумма) — на стороне вызова.
 *
 * @param items Полный набор элементов операции.
 * @param size Размер чанка; <= 0 — один чанк со всем набором (см. chunk()).
 * @param fn Операция над одним чанком; ошибка пробрасывается наружу (частичный прогресс уже учтён).
 * @param onProgress Коллбек прогресса: (обработано, всего) после каждого успешного чанка.
 */
export async function runChunked<TItem, TChunk>(
  items: readonly TItem[],
  size: number,
  fn: (part: TItem[]) => Promise<TChunk>,
  onProgress?: (done: number, total: number) => void,
): Promise<TChunk[]> {
  const total = items.length;
  const results: TChunk[] = [];
  let done = 0;
  for (const part of chunk(items, size)) {
    results.push(await fn(part));
    done += part.length;
    onProgress?.(done, total);
  }
  return results;
}
