// Чистая логика фильтрации отображения слотов — выделена из TguSlotsTab, чтобы покрыть
// юнит-тестами без рендера компонента. Едина для таблицы и карты: обе читают результат
// applySlotFilters (filteredSlots).

import type { RevolutionSign, SlotDTO } from "../../model/slotTypes"

/** Производный статус слота для фильтра видимости. */
export type SlotStatusKey = "free" | "booked" | "occupied"

/** Состояние фильтров отображения. Единая модель «selected = visible» на всех осях:
 *  множество = что ВИДНО; пустое множество = не видно ничего (одинаково для всех осей). */
export type SlotFilterState = {
  status: ReadonlySet<SlotStatusKey>
  satellites: ReadonlySet<number>
  branches: ReadonlySet<RevolutionSign>
}

/**
 * Производный статус слота. Бронь определяется снаружи (isBooked), а не по slot.state,
 * потому что свежезабронированный кандидат приходит со state===AVAILABLE до перечитывания
 * (бронь важнее state). Иначе BUSY (занят бронью на проходе КА) → occupied, остальное → free.
 */
export function slotStatusKey(slot: Pick<SlotDTO, "state">, isBooked: boolean): SlotStatusKey {
  if (isBooked) return "booked"
  if (slot.state === "BUSY") return "occupied"
  return "free"
}

/**
 * Фильтрует слоты по статусу/КА/ветви одним проходом (selected=visible на каждой оси).
 * @param rows Уже суженные по окну суток слоты.
 * @param filters Множества видимых значений по каждой оси.
 * @param isBooked Признак брони по слоту (bookedKeys в компоненте).
 */
export function applySlotFilters(
  rows: readonly SlotDTO[],
  filters: SlotFilterState,
  isBooked: (slot: SlotDTO) => boolean,
): SlotDTO[] {
  return rows.filter(
    (slot) =>
      filters.status.has(slotStatusKey(slot, isBooked(slot))) &&
      filters.satellites.has(slot.satelliteId) &&
      filters.branches.has(slot.revolutionSign),
  )
}
