/**
 * MIXED IMPORT journey M2 — one queue, two cargo kinds. Engine ordering rule
 * under test: CONTAINER bookings keep their staff-set priority scores, BULK
 * priorities are RECOMPUTED from the rule engine at batch time and then order
 * among themselves by acceptance — so the queue is containers-by-priority
 * first, bulk-by-acceptance after. The train fills across both kinds
 * (20w + 20w containers + the first-accepted 14w bulk = 54); the remaining
 * bulk trio waits with no pay window and expires when the day concludes FULL.
 *
 * Sequential steps of one journey — retries off.
 */

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

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

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

const SELECTED: Array<{
  suffix: string;
  freight: "CONTAINER" | "BULK";
  twenty?: number;
  tons?: number;
  wagons: number;
}> = [
  { suffix: "MWC1", freight: "CONTAINER", twenty: 40, wagons: 20 },
  { suffix: "MWC2", freight: "CONTAINER", twenty: 40, wagons: 20 },
  { suffix: "MWB1", freight: "BULK", tons: 980, wagons: 14 },
];
const WAITING = [
  { suffix: "MWB2", tons: 700 },
  { suffix: "MWB3", tons: 700 },
  { suffix: "MWB4", tons: 700 },
];

describe("mixed import: containers + first bulk fill the train, bulk trio waits and expires", { retries: 0 }, () => {
  before(() => {
    cy.task("db:seedFile", "seed-import-corridor.sql");
    SELECTED.forEach((b) =>
      seedImportContract({ suffix: b.suffix, reference: stampedRef(b.suffix), freight: b.freight }),
    );
    WAITING.forEach((b) =>
      seedImportContract({ suffix: b.suffix, reference: stampedRef(b.suffix), freight: "BULK" }),
    );
  });

  it("operations prepares the corridor 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 mixed shipments book; operations accepts — MWB1 is the FIRST-accepted bulk", () => {
    let isoSeed = 9500;
    // Containers first (manual priority), then bulk in acceptance order —
    // MWB1 accepted before the waiters so the recomputed-bulk tie-break
    // (fully_executed_at ASC) puts it at the head of the bulk queue.
    SELECTED.forEach((b) => {
      if (b.freight === "CONTAINER") {
        bookContainers({
          suffix: b.suffix,
          runStamp: stamp,
          isoSeed,
          twenty: b.twenty,
          scheduledDate: BOOKING_DAY,
        });
        isoSeed += b.twenty ?? 0;
      } else {
        bookBulk({ suffix: b.suffix, tons: b.tons!, scheduledDate: BOOKING_DAY });
      }
      acceptOperation(b.suffix);
    });
    WAITING.forEach((b) => {
      bookBulk({ suffix: b.suffix, tons: b.tons, scheduledDate: BOOKING_DAY });
      acceptOperation(b.suffix);
    });
    // Manual priority holds for CONTAINER bookings only (bulk is recomputed).
    setPriority("MWC1", 1);
    setPriority("MWC2", 2);
  });

  it("the batch fills 54 across both kinds; the bulk trio keeps waiting with no pay window", () => {
    withSchedule(DEPARTURE, (s) => {
      closeBookingWindow(s.id);
      completeDocReview(s.id);
    });
    SELECTED.forEach((b) =>
      pollBookingStatus(b.suffix, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
    );
    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 pay — typed allocation 54/54", () => {
    SELECTED.forEach((b) => {
      markPaid(b.suffix);
      pollAllocations(b.suffix, b.wagons);
      expectWagonType(b.suffix, b.freight === "CONTAINER" ? "NW5" : "CW4", b.wagons);
    });
  });

  it("the day concludes FULL — the waiting bulk trio expires in ONE sweep", () => {
    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",
      );
    });
    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 {};
