/**
 * Single source of truth for how a booking window row is presented to a
 * customer or staff list: which status badge to show and which deadline (if
 * any) to count down to.
 *
 * The badge and the countdown MUST be derived together. They used to be
 * computed independently (badge from `isOpenNow`, countdown from
 * `windowPhase`), which let them contradict each other — an export train that
 * filled mid-window kept `windowPhase='OPEN'` (space can free again if a pay
 * window lapses) while `bookingWindowStatus='FULL'`, so the card showed an
 * "Upcoming" badge above a live "Window closes in …" countdown.
 *
 * Phase/status matrix this resolves (server fields on the schedule row):
 *  - windowPhase:   PRE_WINDOW → OPEN → DOC_REVIEW → PAYMENT → DONE
 *                   (export skips the review/payment phases; CLOSED_FOR_DAY is legacy)
 *  - bookingWindowStatus: OPEN | CLOSED | FULL — whether the booking desk
 *                   actually accepts bookings right now.
 */

export type BookingWindowUiKind =
  /** Bookable right now (phase OPEN and the desk flag agrees). */
  | "OPEN"
  /** Train has no capacity left — not bookable; may reopen if a reservation expires. */
  | "FULL"
  /** Announced, opens at `countdownTo`. */
  | "PRE_WINDOW"
  /** Window closed, staff reviewing documents (import cycle). */
  | "DOC_REVIEW"
  /** Batch ran, selected customers are paying (import cycle). */
  | "PAYMENT"
  /** Terminal or not bookable for any other reason. */
  | "CLOSED";

export interface BookingWindowStateInput {
  windowPhase?: string | null;
  bookingWindowStatus?: string | null;
  windowOpensAt?: string | null;
  windowClosesAt?: string | null;
  docReviewEndsAt?: string | null;
  paymentPhaseEndsAt?: string | null;
}

export interface BookingWindowUiState {
  kind: BookingWindowUiKind;
  /** ISO deadline a countdown may tick toward; null = show no countdown. */
  countdownTo: string | null;
  /** True only when the customer can book right now. */
  isBookable: boolean;
}

export function bookingWindowUiState(
  w: BookingWindowStateInput,
): BookingWindowUiState {
  const phase = w.windowPhase ?? null;
  const status = w.bookingWindowStatus ?? null;

  if (phase === "DONE" || phase === "CLOSED_FOR_DAY") {
    return { kind: "CLOSED", countdownTo: null, isBookable: false };
  }

  // Mid-cycle phases win over the FULL flag: the batch may have tentatively
  // filled the train, but an unpaid reservation can still expire and free
  // space, so "document review" / "payment" is the truthful state here.
  if (phase === "DOC_REVIEW") {
    return {
      kind: "DOC_REVIEW",
      countdownTo: w.docReviewEndsAt ?? null,
      isBookable: false,
    };
  }
  if (phase === "PAYMENT") {
    return {
      kind: "PAYMENT",
      countdownTo: w.paymentPhaseEndsAt ?? null,
      isBookable: false,
    };
  }

  // Outside the resolving phases a FULL train is simply not bookable — no
  // countdown either: ticking toward "closes in" would promise a window the
  // customer cannot use.
  if (status === "FULL") {
    return { kind: "FULL", countdownTo: null, isBookable: false };
  }

  if (phase === "PRE_WINDOW") {
    return {
      kind: "PRE_WINDOW",
      countdownTo: w.windowOpensAt ?? null,
      isBookable: false,
    };
  }

  if (phase === "OPEN") {
    if (status === "OPEN") {
      return {
        kind: "OPEN",
        countdownTo: w.windowClosesAt ?? null,
        isBookable: true,
      };
    }
    // Phase says OPEN but the desk flag disagrees (CLOSED): not bookable, and
    // no countdown that pretends otherwise.
    return { kind: "CLOSED", countdownTo: null, isBookable: false };
  }

  return { kind: "CLOSED", countdownTo: null, isBookable: false };
}
