/**
 * BULK IMPORT critical-scenario matrix — bulk-specific corridor edge cases:
 *
 *   1. gate: a wheat booking on a day with no open window is rejected
 *   2. sub-corridor bulk (NAGAD → MOJO, 700 T) rides the through-train next
 *      to a DJIB_PORT → KALITY 1 400 T booking — corridor-aware batch
 *   3. bulk intercity ride-along (MOJO → KALITY, DOMESTIC, 140 T): dateless
 *      booking, staff accept onto the import train's free leg, pay window
 *      opens, paid + linked
 *   4. WHOLE-TRAIN giant: a 4 000 T booking (58 wagons worth) alone on a
 *      54-wagon train → partial offer of the FULL consist (3 780 T), gateway
 *      settle applies the split, the train is FULL from ONE booking, and the
 *      220 T outstanding must be rebooked EXACTLY on a later train.
 *
 * Sequential steps — retries off.
 */

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

const DEPARTURE = departureAt(15);
const BOOKING_DAY = eatDayStr(DEPARTURE);
const GIANT_DEPARTURE = departureAt(16);
const GIANT_DAY = eatDayStr(GIANT_DEPARTURE);
const REMAINDER_DEPARTURE = departureAt(17);
const REMAINDER_DAY = eatDayStr(REMAINDER_DEPARTURE);
const NO_WINDOW_DAY = eatDayStr(departureAt(19)); // no schedule exists there

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

describe("bulk critical matrix: gates, sub-corridor, bulk intercity, whole-train giant", { retries: 0 }, () => {
  before(() => {
    cy.task("db:seedFile", "seed-import-corridor.sql");
    seedImportContract({ suffix: "BM1", reference: stampedRef("BM1"), freight: "BULK" });
    seedImportContract({
      suffix: "BMSUB",
      reference: stampedRef("BMSUB"),
      freight: "BULK",
      originCode: "NAGAD",
      destCode: "MOJO",
    });
    seedImportContract({
      suffix: "BMIC",
      reference: stampedRef("BMIC"),
      freight: "BULK",
      direction: "DOMESTIC",
      originCode: "MOJO",
      destCode: "KALITY",
    });
    seedImportContract({ suffix: "BMG", reference: stampedRef("BMG"), freight: "BULK" });
  });

  it("operations prepares the corridor and the D+15 bulk train with an open window", () => {
    ensureCorridorRoute();
    resetCorridorDay(DEPARTURE);
    resetCorridorDay(GIANT_DEPARTURE);
    resetCorridorDay(REMAINDER_DEPARTURE);
    createImportSchedule({
      departure: DEPARTURE,
      locoPair: ["LOCO-IMP-25", "LOCO-IMP-26"],
      kind: "bulk",
    });
    withSchedule(DEPARTURE, (s) => forceWindowOpen(s.id, 60));
  });

  it("gate: a wheat booking on a day with no open window is rejected", () => {
    bookBulk({
      suffix: "BM1",
      tons: 140,
      scheduledDate: NO_WINDOW_DAY,
      expectFailure: "booking window",
    });
  });

  it("a through-corridor 1 400 T booking and a NAGAD→MOJO 700 T booking share the train", () => {
    bookBulk({ suffix: "BM1", tons: 1400, scheduledDate: BOOKING_DAY });
    acceptOperation("BM1");
    bookBulk({ suffix: "BMSUB", tons: 700, scheduledDate: BOOKING_DAY });
    acceptOperation("BMSUB");

    withSchedule(DEPARTURE, (s) => {
      closeBookingWindow(s.id);
      completeDocReview(s.id);
    });
    ["BM1", "BMSUB"].forEach((suffix) =>
      pollBookingStatus(suffix, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
    );
    markPaid("BM1");
    pollAllocations("BM1", 20);
    markPaid("BMSUB");
    pollAllocations("BMSUB", 10);

    withSchedule(DEPARTURE, (s) => {
      withBooking("BM1", (b) => expect(b.train_schedule_id).to.eq(s.id));
      withBooking("BMSUB", (b) => expect(b.train_schedule_id).to.eq(s.id));
    });
  });

  it("bulk intercity ride-along: dateless DOMESTIC wheat accepted onto the import train's free leg", () => {
    bookBulk({ suffix: "BMIC", tons: 140 }); // 2 wagons MOJO → KALITY, dateless
    acceptOperation("BMIC");

    withSchedule(DEPARTURE, (s) => {
      withBooking("BMIC", (b) => {
        apiPost(opsStaff, `/api/train-scheduling/schedules/${s.id}/intercity/accept`, {
          bookingIds: [b.id],
        })
          .its("status")
          .should("be.oneOf", [200, 201]);
      });
    });
    pollBookingStatus("BMIC", ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]);
    withBooking("BMIC", (b) => {
      expect(b.payment_deadline, "ride-along pay window opened").to.be.a("string");
    });
    markPaid("BMIC");
    withSchedule(DEPARTURE, (s) => {
      withBooking("BMIC", (b) => {
        expect(b.train_schedule_id, "linked to the import train").to.eq(s.id);
        db<{ n: string }>(
          `SELECT count(*) AS n FROM freight.train_schedule_bookings
           WHERE booking_id = $1 AND train_schedule_id = $2 AND deleted_at IS NULL`,
          [b.id, s.id],
        ).then(({ rows }) => expect(Number(rows[0].n), "link row").to.eq(1));
      });
    });
  });

  it("whole-train giant: 4 000 T alone gets a FULL-consist partial offer (3 780 T) and fills the train", () => {
    createImportSchedule({
      departure: GIANT_DEPARTURE,
      locoPair: ["LOCO-IMP-27", "LOCO-IMP-28"],
      kind: "bulk",
    });
    withSchedule(GIANT_DEPARTURE, (s) => forceWindowOpen(s.id, 45));

    bookBulk({ suffix: "BMG", tons: 4000, scheduledDate: GIANT_DAY });
    acceptOperation("BMG");
    withSchedule(GIANT_DEPARTURE, (s) => {
      closeBookingWindow(s.id);
      completeDocReview(s.id);
    });
    pollBookingStatus("BMG", ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]);
    withBooking("BMG", (b) => {
      pollDb<{ status: string }>(
        "BMG open partial offer",
        `SELECT status FROM freight.booking_batch_offers
         WHERE booking_id = $1 AND deleted_at IS NULL
         ORDER BY created_at DESC LIMIT 1`,
        [b.id],
        (row) => row?.status === "OFFERED",
        10,
      );
    });

    settleViaGateway("BMG");
    pollAllocations("BMG", 54);
    withBooking("BMG", (b) => {
      expect(b.is_split, "BMG is split").to.eq(true);
    });
    withSchedule(GIANT_DEPARTURE, (s) => {
      endPaymentPhase(s.id);
      pollDb<ScheduleRow>(
        "giant train FULL + DONE from one booking",
        `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",
      );
    });
  });

  it("the giant's 220 T outstanding must be rebooked EXACTLY on a later train", () => {
    createImportSchedule({
      departure: REMAINDER_DEPARTURE,
      locoPair: ["LOCO-IMP-13", "LOCO-IMP-14"],
      kind: "bulk",
    });
    withSchedule(REMAINDER_DEPARTURE, (s) => forceWindowOpen(s.id, 45));

    bookBulk({
      suffix: "BMG",
      tons: 100,
      scheduledDate: REMAINDER_DAY,
      expectFailure: "must take the whole",
    });
    bookBulk({ suffix: "BMG", tons: 220, scheduledDate: REMAINDER_DAY });
    pollBookingStatus("BMG", "OPERATION_REQUEST_PENDING", 5);
  });
});

export {};
