/**
 * #77 lot F2 — the activity phrasing. Boundaries are calendar-based, so the
 * interesting cases are the ones a 24-hour-span implementation gets wrong.
 *
 * Fixtures are **epoch milliseconds**, the encoding the backend actually sends.
 * They used to be ISO strings, which is precisely why the whole surface passed
 * its tests while rendering "—" against a real server.
 */
import { describe, it, expect } from "vitest";

import { formatExpiresIn, formatLastSeen, formatPairedAt } from "./formatActivity";

/** 2026-07-17 at 09:00 local. */
const NOW = new Date(2026, 6, 17, 9, 0, 0);
const at = (y: number, m: number, d: number, h = 0, min = 0, s = 0) =>
  new Date(y, m, d, h, min, s).getTime();

describe("formatLastSeen", () => {
  it("reads as 'now' within the last couple of minutes", () => {
    expect(formatLastSeen(at(2026, 6, 17, 8, 59), NOW)).toBe("Actif à l'instant");
  });

  it("shows today's time once it is no longer 'now'", () => {
    expect(formatLastSeen(at(2026, 6, 17, 6, 32), NOW)).toBe("Aujourd'hui à 06:32");
  });

  it("says 'Hier' for the previous calendar day, however few hours ago", () => {
    // 23:59 yesterday is 9 hours back: a 24h-window implementation would call
    // this "today", which reads as a lie next to the clock.
    expect(formatLastSeen(at(2026, 6, 16, 23, 59), NOW)).toBe("Hier");
    expect(formatLastSeen(at(2026, 6, 16, 0, 1), NOW)).toBe("Hier");
  });

  it("falls back to a short date beyond yesterday", () => {
    expect(formatLastSeen(at(2026, 6, 12, 14, 0), NOW)).toBe("12 juil.");
  });

  it("adds the year once it is not the current one", () => {
    expect(formatLastSeen(at(2025, 11, 3, 14, 0), NOW)).toBe("3 déc. 2025");
  });

  it("treats a slightly-ahead server clock as 'now', not as the future", () => {
    expect(formatLastSeen(at(2026, 6, 17, 9, 0, 30), NOW)).toBe("Actif à l'instant");
  });

  it("reads a raw epoch-ms number, the way the backend sends it", () => {
    // The regression that started this: a `1784286814274`-shaped value must
    // render a real date, not the "—" an ISO-only parser produced.
    const ms = new Date(2026, 6, 12, 14, 0).getTime();
    expect(Number.isInteger(ms)).toBe(true);
    expect(formatLastSeen(ms, NOW)).toBe("12 juil.");
  });
});

describe("formatPairedAt", () => {
  it("reads as a sentence, not a timestamp", () => {
    expect(formatPairedAt(at(2026, 6, 12), NOW)).toBe("Appairé le 12 juil.");
  });
});

describe("formatExpiresIn", () => {
  const inSeconds = (s: number) => NOW.getTime() + s * 1000;

  it("counts down in minutes over the code's lifetime", () => {
    expect(formatExpiresIn(inSeconds(600), NOW)).toBe("Expire dans 10 min");
    expect(formatExpiresIn(inSeconds(61), NOW)).toBe("Expire dans 2 min");
  });

  it("switches to seconds in the last minute", () => {
    expect(formatExpiresIn(inSeconds(30), NOW)).toBe("Expire dans 30 s");
  });

  it("returns null once expired, so the panel can say so", () => {
    expect(formatExpiresIn(inSeconds(0), NOW)).toBeNull();
    expect(formatExpiresIn(inSeconds(-5), NOW)).toBeNull();
  });
});
