import { describe, expect, it } from "vitest";

import { bookingWindowUiState } from "@edr/ui-common";
import type {
  BookingWindowStateInput,
  BookingWindowUiState,
} from "@edr/ui-common";

/**
 * Scenario table for the shared badge/countdown state. This is the logic that
 * previously let a full export train show an "Upcoming" badge above a live
 * "Window closes in …" countdown — every row asserts badge kind, countdown
 * target, and bookability TOGETHER, so they can never disagree again.
 */

const OPENS = "2026-07-26T05:00:00.000Z";
const CLOSES = "2026-07-27T05:00:00.000Z";
const DOC_ENDS = "2026-07-24T08:30:00.000Z";
const PAY_ENDS = "2026-07-24T09:30:00.000Z";

/** A full row with every timestamp present; scenarios override what they test. */
function row(over: Partial<BookingWindowStateInput>): BookingWindowStateInput {
  return {
    windowPhase: "OPEN",
    bookingWindowStatus: "OPEN",
    windowOpensAt: OPENS,
    windowClosesAt: CLOSES,
    docReviewEndsAt: DOC_ENDS,
    paymentPhaseEndsAt: PAY_ENDS,
    ...over,
  };
}

interface Scenario {
  name: string;
  input: BookingWindowStateInput;
  expected: BookingWindowUiState;
}

const scenarios: Scenario[] = [
  // ---- export FCFS lifecycle -------------------------------------------------
  {
    name: "export announced, before lead window (PRE_WINDOW/CLOSED)",
    input: row({ windowPhase: "PRE_WINDOW", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "PRE_WINDOW", countdownTo: OPENS, isBookable: false },
  },
  {
    name: "export window open, space left (OPEN/OPEN)",
    input: row({}),
    expected: { kind: "OPEN", countdownTo: CLOSES, isBookable: true },
  },
  {
    name: "export filled mid-window (OPEN/FULL) — the reported bug",
    input: row({ bookingWindowStatus: "FULL" }),
    expected: { kind: "FULL", countdownTo: null, isBookable: false },
  },
  {
    name: "export space freed after an expiry cleared FULL (OPEN/OPEN again)",
    input: row({}),
    expected: { kind: "OPEN", countdownTo: CLOSES, isBookable: true },
  },
  {
    name: "export window over (DONE/CLOSED)",
    input: row({ windowPhase: "DONE", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "export departed while full (DONE/FULL)",
    input: row({ windowPhase: "DONE", bookingWindowStatus: "FULL" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },

  // ---- import daily cycle ----------------------------------------------------
  {
    name: "import before booking day (PRE_WINDOW/CLOSED)",
    input: row({ windowPhase: "PRE_WINDOW", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "PRE_WINDOW", countdownTo: OPENS, isBookable: false },
  },
  {
    name: "import window open (OPEN/OPEN)",
    input: row({}),
    expected: { kind: "OPEN", countdownTo: CLOSES, isBookable: true },
  },
  {
    name: "import window closed, staff reviewing docs (DOC_REVIEW/CLOSED)",
    input: row({ windowPhase: "DOC_REVIEW", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "DOC_REVIEW", countdownTo: DOC_ENDS, isBookable: false },
  },
  {
    name: "import payment phase, selected customers paying (PAYMENT/CLOSED)",
    input: row({ windowPhase: "PAYMENT", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "PAYMENT", countdownTo: PAY_ENDS, isBookable: false },
  },
  {
    name: "import batch tentatively filled the train (PAYMENT/FULL) — phase wins, unpaid may still free space",
    input: row({ windowPhase: "PAYMENT", bookingWindowStatus: "FULL" }),
    expected: { kind: "PAYMENT", countdownTo: PAY_ENDS, isBookable: false },
  },
  {
    name: "import doc review while flag already FULL (DOC_REVIEW/FULL) — phase wins",
    input: row({ windowPhase: "DOC_REVIEW", bookingWindowStatus: "FULL" }),
    expected: { kind: "DOC_REVIEW", countdownTo: DOC_ENDS, isBookable: false },
  },
  {
    name: "import reopen cycle scheduled (PRE_WINDOW/CLOSED, cycle 2)",
    input: row({ windowPhase: "PRE_WINDOW", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "PRE_WINDOW", countdownTo: OPENS, isBookable: false },
  },
  {
    name: "import reopen refused while train still FULL (PRE_WINDOW/FULL)",
    input: row({ windowPhase: "PRE_WINDOW", bookingWindowStatus: "FULL" }),
    expected: { kind: "FULL", countdownTo: null, isBookable: false },
  },
  {
    name: "import train full and finalized (DONE/FULL)",
    input: row({ windowPhase: "DONE", bookingWindowStatus: "FULL" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "import no cycle fits before departure (DONE/CLOSED)",
    input: row({ windowPhase: "DONE", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "legacy closed-for-the-day row (CLOSED_FOR_DAY/CLOSED)",
    input: row({ windowPhase: "CLOSED_FOR_DAY", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "legacy closed-for-the-day row while full (CLOSED_FOR_DAY/FULL)",
    input: row({ windowPhase: "CLOSED_FOR_DAY", bookingWindowStatus: "FULL" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },

  // ---- desync / stale rows ---------------------------------------------------
  {
    name: "phase OPEN but desk flag CLOSED (desync) — closed, no countdown",
    input: row({ bookingWindowStatus: "CLOSED" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "dispatched train stuck at OPEN/CLOSED (tick skips non-scheduled rows)",
    input: row({ bookingWindowStatus: "CLOSED" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "FULL flag with no phase at all (legacy pre-window-engine row)",
    input: row({ windowPhase: null, bookingWindowStatus: "FULL" }),
    expected: { kind: "FULL", countdownTo: null, isBookable: false },
  },
  {
    name: "legacy row, no phase, desk open (null/OPEN) — not phase-driven, shows closed",
    input: row({ windowPhase: null }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "unknown future phase value — safe fallback to closed",
    input: row({ windowPhase: "SOMETHING_NEW" }),
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },

  // ---- missing timestamps (no countdown, badge still right) -------------------
  {
    name: "PRE_WINDOW without an opens-at timestamp",
    input: row({
      windowPhase: "PRE_WINDOW",
      bookingWindowStatus: "CLOSED",
      windowOpensAt: null,
    }),
    expected: { kind: "PRE_WINDOW", countdownTo: null, isBookable: false },
  },
  {
    name: "OPEN without a closes-at timestamp",
    input: row({ windowClosesAt: null }),
    expected: { kind: "OPEN", countdownTo: null, isBookable: true },
  },
  {
    name: "DOC_REVIEW without an ends-at timestamp",
    input: row({
      windowPhase: "DOC_REVIEW",
      bookingWindowStatus: "CLOSED",
      docReviewEndsAt: null,
    }),
    expected: { kind: "DOC_REVIEW", countdownTo: null, isBookable: false },
  },
  {
    name: "PAYMENT without an ends-at timestamp",
    input: row({
      windowPhase: "PAYMENT",
      bookingWindowStatus: "CLOSED",
      paymentPhaseEndsAt: null,
    }),
    expected: { kind: "PAYMENT", countdownTo: null, isBookable: false },
  },
  {
    name: "row with every field null",
    input: {
      windowPhase: null,
      bookingWindowStatus: null,
      windowOpensAt: null,
      windowClosesAt: null,
      docReviewEndsAt: null,
      paymentPhaseEndsAt: null,
    },
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },
  {
    name: "row with every field undefined (structural minimum)",
    input: {},
    expected: { kind: "CLOSED", countdownTo: null, isBookable: false },
  },

  // ---- countdown targets track the right deadline per phase -------------------
  {
    name: "PRE_WINDOW counts to opens-at, not closes-at",
    input: row({ windowPhase: "PRE_WINDOW", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "PRE_WINDOW", countdownTo: OPENS, isBookable: false },
  },
  {
    name: "OPEN counts to closes-at, not doc review",
    input: row({}),
    expected: { kind: "OPEN", countdownTo: CLOSES, isBookable: true },
  },
  {
    name: "DOC_REVIEW counts to review end, not payment end",
    input: row({ windowPhase: "DOC_REVIEW", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "DOC_REVIEW", countdownTo: DOC_ENDS, isBookable: false },
  },
  {
    name: "PAYMENT counts to payment end, not window close",
    input: row({ windowPhase: "PAYMENT", bookingWindowStatus: "CLOSED" }),
    expected: { kind: "PAYMENT", countdownTo: PAY_ENDS, isBookable: false },
  },
];

describe("bookingWindowUiState", () => {
  it.each(scenarios)("$name", ({ input, expected }) => {
    expect(bookingWindowUiState(input)).toEqual(expected);
  });

  it("never yields a countdown on a non-bookable FULL state, whatever else is set", () => {
    for (const phase of ["OPEN", "PRE_WINDOW", null, "ANYTHING"]) {
      const state = bookingWindowUiState(row({ windowPhase: phase, bookingWindowStatus: "FULL" }));
      expect(state.kind).toBe("FULL");
      expect(state.countdownTo).toBeNull();
      expect(state.isBookable).toBe(false);
    }
  });

  it("is bookable ONLY when phase and desk flag are both OPEN", () => {
    const combos: Array<[string | null, string | null]> = [];
    for (const phase of ["PRE_WINDOW", "OPEN", "DOC_REVIEW", "PAYMENT", "DONE", "CLOSED_FOR_DAY", null]) {
      for (const status of ["OPEN", "CLOSED", "FULL", null]) {
        combos.push([phase, status]);
      }
    }
    for (const [phase, status] of combos) {
      const state = bookingWindowUiState(
        row({ windowPhase: phase, bookingWindowStatus: status }),
      );
      expect(state.isBookable).toBe(phase === "OPEN" && status === "OPEN");
    }
  });
});
