/**
 * SCHEDULING — window-rule snapshot survives a global-rule edit:
 *
 *   Each `train_schedules` row freezes the booking-window rule it was CREATED
 *   with, into 5 `rule_*` columns (migration 1920000000000). Editing the
 *   global rules must apply to FUTURE schedules only — an already-created
 *   schedule keeps its OWN frozen rule, so the batch board/customer window
 *   never redraws under an already-open train (see the
 *   schedule-window-rule-snapshot memory for the regression this protects:
 *   a global-rule edit used to redraw an open schedule's board as a
 *   synthetic grid that no longer matched the window the customer saw).
 *
 *     – schedule A is created under the CURRENT global rules → its snapshot
 *       matches them
 *     – PATCH /global-rules to DIFFERENT windowOpenHour/windowDurationHours
 *     – schedule A's snapshot is UNCHANGED (frozen) after the edit
 *     – schedule B, created AFTER the edit, snapshots the NEW values
 *     – global rules are restored at the end — a shared singleton every
 *       other spec in the suite reads
 *
 * Retries off — sequential steps against the shared global-rules singleton.
 */

import {
  apiGet,
  apiPatch,
  createImportSchedule,
  db,
  departureAt,
  ensureCorridorRoute,
  forceWindowOpen,
  resetCorridorDay,
  superAdmin,
  withSchedule,
} from "./import-utils";

const DAY_A = departureAt(30);
const DAY_B = departureAt(31);

interface GlobalRules {
  windowOpenHour: number;
  windowDurationHours: number;
}

interface SnapshotRow {
  rule_window_open_hour: number;
  rule_window_duration_hours: string;
}

function snapshotFor(departure: Date, fn: (row: SnapshotRow) => void) {
  withSchedule(departure, (s) => {
    db<SnapshotRow>(
      `SELECT rule_window_open_hour, rule_window_duration_hours
         FROM freight.train_schedules WHERE id = $1`,
      [s.id],
    ).then(({ rows }) => {
      expect(rows, "schedule snapshot row").to.have.length(1);
      fn(rows[0]);
    });
  });
}

let original: GlobalRules;
let changed: GlobalRules;

describe("scheduling: window-rule snapshot is frozen against later global-rule edits", { retries: 0 }, () => {
  before(() => {
    cy.task("db:seedFile", "seed-import-corridor.sql");
    ensureCorridorRoute();
    resetCorridorDay(DAY_A);
    resetCorridorDay(DAY_B);
  });

  after(() => {
    // Restore the shared global-rules singleton for every other spec.
    if (original) apiPatch(superAdmin, "/api/train-scheduling/global-rules", original);
  });

  it("captures the current global rules, then creates schedule A under them", () => {
    apiGet(superAdmin, "/api/train-scheduling/global-rules").then((res) => {
      const body = res.body.data ?? res.body;
      original = {
        windowOpenHour: Number(body.windowOpenHour),
        windowDurationHours: Number(body.windowDurationHours),
      };
      changed = {
        windowOpenHour: (original.windowOpenHour + 5) % 24,
        windowDurationHours: original.windowDurationHours >= 6 ? 2 : 8,
      };
      expect(changed.windowOpenHour, "changed open hour differs").to.not.eq(original.windowOpenHour);
      expect(changed.windowDurationHours, "changed duration differs").to.not.eq(
        original.windowDurationHours,
      );
    });

    createImportSchedule({ departure: DAY_A, locoPair: ["LOCO-IMP-1", "LOCO-IMP-2"] });
    // restampPendingWindows refreshes the snapshot for PRE_WINDOW schedules —
    // the freeze only takes hold once a schedule is OPEN (see the
    // schedule-window-rule-snapshot memory). Force it open now so the global-
    // rule edit below lands on an already-frozen schedule, not a pending one.
    withSchedule(DAY_A, (s) => forceWindowOpen(s.id, 45));

    snapshotFor(DAY_A, (row) => {
      expect(row.rule_window_open_hour, "A snapshots current open hour").to.eq(
        original.windowOpenHour,
      );
      expect(Number(row.rule_window_duration_hours), "A snapshots current duration").to.eq(
        original.windowDurationHours,
      );
    });
  });

  it("edits the global rules, creates schedule B — B snapshots the NEW values", () => {
    apiPatch(superAdmin, "/api/train-scheduling/global-rules", changed).then((res) =>
      expect(res.status, "global rules updated").to.be.oneOf([200, 201]),
    );

    createImportSchedule({ departure: DAY_B, locoPair: ["LOCO-IMP-3", "LOCO-IMP-4"] });

    snapshotFor(DAY_B, (row) => {
      expect(row.rule_window_open_hour, "B snapshots the NEW open hour").to.eq(
        changed.windowOpenHour,
      );
      expect(Number(row.rule_window_duration_hours), "B snapshots the NEW duration").to.eq(
        changed.windowDurationHours,
      );
    });
  });

  it("schedule A's snapshot is UNCHANGED — frozen against the later edit", () => {
    snapshotFor(DAY_A, (row) => {
      expect(row.rule_window_open_hour, "A still the ORIGINAL open hour").to.eq(
        original.windowOpenHour,
      );
      expect(Number(row.rule_window_duration_hours), "A still the ORIGINAL duration").to.eq(
        original.windowDurationHours,
      );
    });
  });
});

export {};
