import {
	datetimeField,
	datetimeLocalToUnix,
	editDatetimeField,
	resolveDatetimeField,
	unixToDatetimeLocal,
} from "./datetime";

/**
 * Every assertion here is about local time, so the zone has to be pinned. New
 * York is the zone the DST cases are written for: the fold is 2024-11-03 01:30
 * and the gap is 2024-03-10 02:30.
 */
// The app never reads `process`, so `src` is typed without node's globals; the
// test host is node, where assigning `TZ` re-reads the zone for `Date`.
declare const process: { env: Record<string, string | undefined> };

const originalTz = process.env["TZ"];
beforeAll(() => {
	process.env["TZ"] = "America/New_York";
});
afterAll(() => {
	process.env["TZ"] = originalTz;
});

/** 2024-06-01T12:34:56 EDT. */
const SUMMER = 1_717_259_696;

test("a non-zero-second instant round trips", () => {
	expect(unixToDatetimeLocal(SUMMER)).toBe("2024-06-01T12:34:56");
	expect(datetimeLocalToUnix("2024-06-01T12:34:56")).toBe(SUMMER);
});

test("text without seconds parses as :00", () => {
	expect(datetimeLocalToUnix("2024-06-01T12:34")).toBe(datetimeLocalToUnix("2024-06-01T12:34:00"));
});

test("text that is not a datetime-local value names no instant", () => {
	expect(datetimeLocalToUnix("")).toBeUndefined();
	expect(datetimeLocalToUnix("yesterday")).toBeUndefined();
	expect(datetimeLocalToUnix("2024-06-01")).toBeUndefined();
	expect(datetimeLocalToUnix("2024-13-01T00:00:00")).toBeUndefined();
});

/** 2024-11-03 01:30 EDT and 01:30 EST: two instants, one wall clock. */
const FOLD_FIRST = 1_730_611_800;
const FOLD_SECOND = 1_730_615_400;

test("the fall-back fold gives two instants the same text", () => {
	expect(unixToDatetimeLocal(FOLD_FIRST)).toBe("2024-11-03T01:30:00");
	expect(unixToDatetimeLocal(FOLD_SECOND)).toBe("2024-11-03T01:30:00");
	expect(datetimeLocalToUnix("2024-11-03T01:30:00")).toBe(FOLD_FIRST);
});

test("an untouched fold bound applies the instant it was seeded with, not a re-parse of its text", () => {
	const field = datetimeField(FOLD_SECOND);
	expect(field.text).toBe("2024-11-03T01:30:00");
	expect(resolveDatetimeField(field)).toEqual({ ok: true, value: FOLD_SECOND });
});

test("an edited fold bound resolves to the first of the two instants, which is what its text says", () => {
	const field = editDatetimeField(datetimeField(FOLD_SECOND), "2024-11-03T01:30:00");
	expect(resolveDatetimeField(field)).toEqual({ ok: true, value: FOLD_FIRST });
});

test("a spring-forward time that exists on no clock is rejected rather than slid forward an hour", () => {
	const field = editDatetimeField(datetimeField(undefined), "2024-03-10T02:30:00");
	expect(datetimeLocalToUnix("2024-03-10T02:30:00")).toBe(1_710_055_800);
	expect(unixToDatetimeLocal(1_710_055_800)).toBe("2024-03-10T03:30:00");
	expect(resolveDatetimeField(field)).toEqual({ ok: false, reason: "nonexistent" });
});

test("an edited bound round trips with non-zero seconds", () => {
	const field = editDatetimeField(datetimeField(undefined), "2024-06-01T12:34:56");
	expect(resolveDatetimeField(field)).toEqual({ ok: true, value: SUMMER });
});

test("clearing an edited bound drops the filter", () => {
	const field = editDatetimeField(datetimeField(SUMMER), "");
	expect(resolveDatetimeField(field)).toEqual({ ok: true, value: undefined });
});

test("an unparseable edit is reported, never silently dropped", () => {
	const field = editDatetimeField(datetimeField(undefined), "2024-06-32T99:99");
	expect(resolveDatetimeField(field)).toEqual({ ok: false, reason: "unparseable" });
});

test("an unset bound seeds an empty field that stays unset", () => {
	const field = datetimeField(undefined);
	expect(field.text).toBe("");
	expect(resolveDatetimeField(field)).toEqual({ ok: true, value: undefined });
});

test("a fractional part on the seconds is parsed and dropped, not rejected", () => {
	// jsdom, and any engine that sanitizes to the full grammar, hands the input
	// back with milliseconds attached; a bound is a whole second either way.
	const field = editDatetimeField(datetimeField(undefined), "2023-11-14T23:13:37.000");
	const resolved = resolveDatetimeField(field);
	expect(resolved).toEqual({ ok: true, value: datetimeLocalToUnix("2023-11-14T23:13:37") });
});
