// Чистая логика выбора слотов и разбиения на чанки — выделена из TguSlotsTab,
// чтобы покрыть юнит-тестами без рендера компонента.

/**
 * Новое множество выбора после клика по строке.
 * Обычный клик — переключает один ключ. Shift+клик при заданном якоре — выбирает (или
 * снимает) непрерывный диапазон от якоря до текущего индекса; направление задаётся новым
 * состоянием текущей строки (если она была не выбрана — диапазон выбираем, иначе снимаем).
 *
 * @param current Текущее множество выбранных ключей.
 * @param keyAt Доступ к ключу слота по индексу в видимом (отфильтрованном) списке.
 * @param index Индекс строки, по которой кликнули.
 * @param shiftKey Был ли зажат Shift.
 * @param anchorIndex Индекс предыдущего клика (якорь) либо null.
 */
export function selectionAfterCheck(
  current: ReadonlySet<string>,
  keyAt: (index: number) => string,
  index: number,
  shiftKey: boolean,
  anchorIndex: number | null,
): Set<string> {
  const next = new Set(current)
  const targetKey = keyAt(index)
  if (shiftKey && anchorIndex != null) {
    const lo = Math.min(anchorIndex, index)
    const hi = Math.max(anchorIndex, index)
    const select = !current.has(targetKey)
    for (let i = lo; i <= hi; i++) {
      const k = keyAt(i)
      if (select) next.add(k)
      else next.delete(k)
    }
  } else if (next.has(targetKey)) {
    next.delete(targetKey)
  } else {
    next.add(targetKey)
  }
  return next
}

/**
 * Новое множество выбора после жеста «полоса от первого к последнему»: выбор полосы становится
 * РОВНО диапазоном [fromKey..toKey] (в порядке времени). Члены полосы вне диапазона снимаются —
 * так Shift+клик по слоту ближе к якорю «сжимает» выделение. Выбор за пределами полосы (другие
 * полосы/КА) сохраняется: снимаем/добавляем только ключи из members. Жест ограничен одной полосой:
 * вызывающий передаёт members именно её (один КА, один крен, подряд по времени); граница полосы —
 * гарантия «нельзя выделить не полосу». Если fromKey или toKey не из members — выбор не меняется
 * (страховка). Вход не мутируется.
 *
 * @param current Текущее множество выбранных ключей.
 * @param members Ключи членов полосы в порядке времени.
 * @param fromKey Ключ якоря (первый клик).
 * @param toKey Ключ второго клика.
 */
export function selectStripRange(
  current: ReadonlySet<string>,
  members: readonly string[],
  fromKey: string,
  toKey: string,
): Set<string> {
  const next = new Set(current)
  const from = members.indexOf(fromKey)
  const to = members.indexOf(toKey)
  if (from < 0 || to < 0) return next
  const lo = Math.min(from, to)
  const hi = Math.max(from, to)
  members.forEach((key, i) => {
    if (i >= lo && i <= hi) next.add(key)
    else next.delete(key)
  })
  return next
}

/**
 * Снимает выбор с диапазона полосы [fromKey..toKey] (в порядке времени). Зеркало selectStripRange:
 * убирает ровно члены диапазона, не трогая остальной выбор. Нужно для повторного Shift+клика по тому
 * же концу — он снимает выделенный диапазон. Если fromKey/toKey не из members — выбор не меняется.
 *
 * @param current Текущее множество выбранных ключей.
 * @param members Ключи членов полосы в порядке времени.
 * @param fromKey Ключ якоря (первый клик).
 * @param toKey Ключ второго клика.
 */
export function deselectStripRange(
  current: ReadonlySet<string>,
  members: readonly string[],
  fromKey: string,
  toKey: string,
): Set<string> {
  const next = new Set(current)
  const from = members.indexOf(fromKey)
  const to = members.indexOf(toKey)
  if (from < 0 || to < 0) return next
  const lo = Math.min(from, to)
  const hi = Math.max(from, to)
  for (let i = lo; i <= hi; i++) next.delete(members[i])
  return next
}

/**
 * Разбивает массив на чанки заданного размера (для чанкованной брони крупных наборов).
 * @param items Исходный массив.
 * @param size Размер чанка; <= 0 — один чанк со всем массивом.
 */
export function chunk<T>(items: readonly T[], size: number): T[][] {
  if (size <= 0) return items.length ? [items.slice()] : []
  const out: T[][] = []
  for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size))
  return out
}
