/**
 * IMPORT journey 4 — nobody pays in the first window cycle: every reserved
 * booking expires, the cycle concludes NOT-full and the window REOPENS for a
 * second cycle on the same train. A fresh booking arrives in cycle 2, pays,
 * and allocates — the train recovers from a dead first window.
 *
 * Sequential steps of one journey — retries off.
 */

import {
  resetCorridorDay,
  acceptOperation,
  bookContainers,
  closeBookingWindow,
  completeDocReview,
  createImportSchedule,
  departureAt,
  eatDayStr,
  endPaymentPhase,
  ensureCorridorRoute,
  forceReservationExpiry,
  forceWindowOpen,
  markPaid,
  pollAllocations,
  pollBookingStatus,
  pollDb,
  seedImportContract,
  withBooking,
  withSchedule,
  type ScheduleRow,
} from "./import-utils";

const DEPARTURE = departureAt(7);
const BOOKING_DAY = eatDayStr(DEPARTURE);

const stamp = String(Date.now());
const stampedRef = (suffix: string) => `CTR-IMP-${stamp}-${suffix}`;

describe("import: dead first cycle — expire all, reopen, book again", { retries: 0 }, () => {
  before(() => {
    cy.task("db:seedFile", "seed-import-corridor.sql");
    ["RA", "RB", "RC"].forEach((suffix) =>
      seedImportContract({ suffix, reference: stampedRef(suffix) }),
    );
  });

  it("operations prepares the corridor train — first window opens (cycle 1)", () => {
    ensureCorridorRoute();
    resetCorridorDay(DEPARTURE);
    createImportSchedule({ departure: DEPARTURE, locoPair: ["LOCO-IMP-9", "LOCO-IMP-10"] });
    withSchedule(DEPARTURE, (s) => forceWindowOpen(s.id, 45));
    withSchedule(DEPARTURE, (s) => expect(s.booking_cycle_no, "cycle 1").to.eq(1));
  });

  it("two customers book and are reserved in cycle 1", () => {
    bookContainers({
      suffix: "RA",
      runStamp: stamp,
      isoSeed: 4000,
      twenty: 40,
      scheduledDate: BOOKING_DAY,
    });
    bookContainers({
      suffix: "RB",
      runStamp: stamp,
      isoSeed: 4100,
      forty: 20,
      scheduledDate: BOOKING_DAY,
    });
    ["RA", "RB"].forEach((suffix) => acceptOperation(suffix));
    withSchedule(DEPARTURE, (s) => {
      closeBookingWindow(s.id);
      completeDocReview(s.id);
    });
    ["RA", "RB"].forEach((suffix) =>
      pollBookingStatus(suffix, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
    );
  });

  it("nobody pays — both reservations expire and the cycle concludes not-full", () => {
    ["RA", "RB"].forEach((suffix) => forceReservationExpiry(suffix));
    withSchedule(DEPARTURE, (s) => {
      endPaymentPhase(s.id);
      // Not full + departure days away → the engine schedules a fresh cycle.
      // When the reopen instant falls inside office hours, the 10s tick's
      // guard-loop chains PRE_WINDOW straight into OPEN within the SAME tick
      // (booking-window.service.ts advanceSchedule), so PRE_WINDOW is not a
      // reliably observable resting state — assert the cycle left PAYMENT
      // without concluding FULL/DONE, whichever phase it lands on.
      pollDb<ScheduleRow>(
        "window concludes not-full (PRE_WINDOW or fast-forwarded to OPEN)",
        `SELECT window_phase FROM freight.train_schedules WHERE id = $1`,
        [s.id],
        (row) => !!row && row.window_phase !== "PAYMENT" && row.window_phase !== "DONE",
      );
    });
  });

  it("the second window opens (cycle 2) and a fresh booking pays and allocates", () => {
    withSchedule(DEPARTURE, (s) => forceWindowOpen(s.id, 45));
    withSchedule(DEPARTURE, (s) => expect(s.booking_cycle_no, "cycle 2").to.eq(2));

    bookContainers({
      suffix: "RC",
      runStamp: stamp,
      isoSeed: 4200,
      twenty: 20,
      scheduledDate: BOOKING_DAY,
    });
    acceptOperation("RC");
    withSchedule(DEPARTURE, (s) => {
      closeBookingWindow(s.id);
      completeDocReview(s.id);
    });
    pollBookingStatus("RC", ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]);
    markPaid("RC");
    pollAllocations("RC", 10);

    // The dead cycle's corpses stay dead; the recovery booking is on the train.
    ["RA", "RB"].forEach((suffix) =>
      withBooking(suffix, (b) => expect(b.status, `${suffix} stays expired`).to.eq("EXPIRED")),
    );
    withSchedule(DEPARTURE, (s) => {
      withBooking("RC", (b) => {
        expect(b.train_schedule_id, "RC rides the reopened train").to.eq(s.id);
      });
    });
  });
});

export {};
