/**
 * IMPORT journey 2 — the train fills from THREE bookings; three more sit in
 * the waiting pool of the same (first) window. The three selected bookings
 * pay and allocate; when the cycle concludes with the train FULL, the three
 * waiting bookings have nowhere left to go on the day and expire.
 *
 * Wagon math (54-wagon consist):
 *   selected: WA 40×20ft = 20w, WB 20×40ft = 20w, WC 28×20ft = 14w  → Σ 54
 *   waiting:  WW1 20×20ft = 10w, WW2 10×40ft = 10w, WW3 20×20ft = 10w
 *
 * Priority order (score DESC drives the batch): WA > WB > WC > WW1 > WW2 > WW3.
 *
 * Sequential steps of one journey — retries off.
 */

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

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

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

const SELECTED = [
  { suffix: "WA", twenty: 40, forty: 0, wagons: 20 },
  { suffix: "WB", twenty: 0, forty: 20, wagons: 20 },
  { suffix: "WC", twenty: 28, forty: 0, wagons: 14 },
];
const WAITING = [
  { suffix: "WW1", twenty: 20, forty: 0, wagons: 10 },
  { suffix: "WW2", twenty: 0, forty: 10, wagons: 10 },
  { suffix: "WW3", twenty: 20, forty: 0, wagons: 10 },
];
const ALL = [...SELECTED, ...WAITING];

describe("import: 3 bookings fill the train, 3 wait and expire", { retries: 0 }, () => {
  before(() => {
    cy.task("db:seedFile", "seed-import-corridor.sql");
    ALL.forEach((b) =>
      seedImportContract({ suffix: b.suffix, reference: stampedRef(b.suffix) }),
    );
  });

  it("operations prepares the corridor and a 54-wagon train with an open first window", () => {
    ensureCorridorRoute();
    resetCorridorDay(DEPARTURE);
    createImportSchedule({ departure: DEPARTURE, locoPair: ["LOCO-IMP-3", "LOCO-IMP-4"] });
    withSchedule(DEPARTURE, (s) => forceWindowOpen(s.id, 45));
  });

  it("six customers book in the first window; operations accepts all six", () => {
    let isoSeed = 500;
    ALL.forEach((b) => {
      bookContainers({
        suffix: b.suffix,
        runStamp: stamp,
        isoSeed,
        twenty: b.twenty,
        forty: b.forty,
        scheduledDate: BOOKING_DAY,
      });
      isoSeed += b.twenty + b.forty;
      acceptOperation(b.suffix);
    });
    ALL.forEach((b, i) => setPriority(b.suffix, i + 1));
  });

  it("the batch selects exactly the three that fill 54 wagons; the rest keep waiting", () => {
    withSchedule(DEPARTURE, (s) => {
      closeBookingWindow(s.id);
      completeDocReview(s.id);
    });
    SELECTED.forEach((b) =>
      pollBookingStatus(b.suffix, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
    );
    // Waiting bookings stay in the pool: FULLY_EXECUTED, no pay window opened.
    WAITING.forEach((b) =>
      withBooking(b.suffix, (row) => {
        expect(row.status, `${b.suffix} still waiting`).to.eq("FULLY_EXECUTED");
        expect(row.payment_deadline, `${b.suffix} has no pay deadline`).to.be.null;
      }),
    );
  });

  it("the three selected bookings pay and allocate — 54/54", () => {
    SELECTED.forEach((b) => {
      markPaid(b.suffix);
      pollAllocations(b.suffix, b.wagons);
    });
    withSchedule(DEPARTURE, (s) => {
      db<{ n: string }>(
        `SELECT count(DISTINCT wba.train_set_wagon_id) AS n
         FROM freight.wagon_booking_allocations wba
         JOIN freight.train_schedule_bookings tsb
           ON tsb.booking_id = wba.booking_id AND tsb.train_schedule_id = $1
         WHERE wba.deleted_at IS NULL AND tsb.deleted_at IS NULL`,
        [s.id],
      ).then(({ rows }) => expect(Number(rows[0].n), "54 wagons allocated").to.eq(54));
    });
  });

  it("the cycle concludes FULL — the three waiting bookings expire with the day", () => {
    withSchedule(DEPARTURE, (s) => {
      endPaymentPhase(s.id);
      pollDb<ScheduleRow>(
        "window FULL + DONE",
        `SELECT window_phase, booking_window_status FROM freight.train_schedules WHERE id = $1`,
        [s.id],
        (row) => row?.booking_window_status === "FULL" && row?.window_phase === "DONE",
      );
    });
    // No sibling train on the route-day can take them → the leftover day pool
    // expires (the paid three are untouched).
    WAITING.forEach((b) => pollBookingStatus(b.suffix, "EXPIRED"));
    SELECTED.forEach((b) =>
      withBooking(b.suffix, (row) => expect(row.status, `${b.suffix} stays PAID`).to.eq("PAID")),
    );
  });
});

export {};
