import { describe, it, expect, beforeEach, beforeAll, vi } from "vitest";
import {
  getRecentErrorLogs,
  getLogs,
  clearLogs,
  initLogCapture,
} from "./logService";

beforeAll(() => {
  // Patch console.* so addEntry runs. Idempotent.
  initLogCapture();
});

describe("getRecentErrorLogs", () => {
  beforeEach(() => {
    // Reset the in-memory buffer. clearLogs also clears sessionStorage
    // which jsdom provides in vitest.
    clearLogs();
  });

  it("returns an empty string when the log buffer is empty", () => {
    expect(getRecentErrorLogs(5)).toBe("");
  });

  it("returns an empty string when n <= 0", () => {
    console.error("boom");
    expect(getRecentErrorLogs(0)).toBe("");
    expect(getRecentErrorLogs(-3)).toBe("");
  });

  it("filters out info-level entries", () => {
    // Freeze time so the ISO prefix is predictable
    vi.setSystemTime(new Date("2026-04-17T15:00:00.000Z"));
    console.log("just chatter");
    console.warn("low fuel");
    console.error("engine out");

    const out = getRecentErrorLogs(10);
    expect(out).not.toContain("chatter");
    expect(out).toContain("WARN: low fuel");
    expect(out).toContain("ERROR: engine out");
    vi.useRealTimers();
  });

  it("keeps only the last N non-info entries in order", () => {
    for (let i = 0; i < 5; i++) console.warn(`w${i}`);
    const out = getRecentErrorLogs(2);
    const lines = out.split("\n");
    expect(lines).toHaveLength(2);
    expect(lines[0]).toContain("w3");
    expect(lines[1]).toContain("w4");
  });

  it("formats each line as `[ISO] LEVEL: message`", () => {
    vi.setSystemTime(new Date("2026-04-17T15:23:45.000Z"));
    console.error("export failed");
    const out = getRecentErrorLogs(1);
    expect(out).toMatch(/^\[2026-04-17T15:23:45\.000Z\] ERROR: export failed$/);
    vi.useRealTimers();
  });
});

// Regression: the log console (Settings → Systems → Journaux) stayed empty
// because getLogs returned the same mutated array reference on every call, so
// useSyncExternalStore (which compares snapshots by identity) never re-rendered
// when a new log arrived. getLogs must return a STABLE reference between
// mutations (no per-call re-slice, or React loops) and a NEW one after each.
describe("getLogs — live-update snapshot", () => {
  beforeEach(() => {
    clearLogs();
  });

  it("returns a stable reference between mutations", () => {
    expect(getLogs()).toBe(getLogs());
    console.warn("x");
    const snap = getLogs();
    expect(snap).toBe(getLogs());
  });

  it("returns a NEW reference after a log so the store re-renders", () => {
    const before = getLogs();
    console.warn("new entry");
    const after = getLogs();
    expect(after).not.toBe(before);
    expect(after).toHaveLength(before.length + 1);
    expect(after[after.length - 1].message).toBe("new entry");
  });

  it("returns a NEW reference after clearLogs", () => {
    console.warn("y");
    const before = getLogs();
    clearLogs();
    const after = getLogs();
    expect(after).not.toBe(before);
    expect(after).toHaveLength(0);
  });
});
