import { describe, expect, test } from "bun:test";
import {
  createAcknowledgement,
  createRealtimeHistory,
  createRealtimeMessage,
  realtimeSseResponse,
} from "../src/index.ts";

describe("realtime replay and acknowledgements", () => {
  test("bounds room history and resumes after a client acknowledgement", () => {
    const history = createRealtimeHistory({ limitPerRoom: 2 });
    const entries = [1, 2, 3].map((value) =>
      history.publish(
        "chat",
        createRealtimeMessage({ type: "message", room: "chat", data: { value } }),
      ),
    );
    expect(history.replay("chat").map((entry) => entry.message.data)).toEqual([
      { value: 2 },
      { value: 3 },
    ]);
    history.acknowledge("chat", "client-1", entries[1]!.sequence);
    expect(history.resume("chat", "client-1")).toHaveLength(1);
    expect(history.snapshot()).toMatchObject({ rooms: 1, messages: 2, acknowledgements: 1 });
  });
  test("acknowledgements are monotonic and reject impossible values", () => {
    const history = createRealtimeHistory();
    const entry = history.publish("chat", createRealtimeMessage({ type: "message", data: {} }));
    history.acknowledge("chat", "client", entry.sequence);
    history.acknowledge("chat", "client", 0);
    expect(history.acknowledged("chat", "client")).toBe(entry.sequence);
    expect(() => history.acknowledge("chat", "client", entry.sequence + 1)).toThrow();
    expect(createAcknowledgement("chat", entry.sequence, "client").type).toBe("ack");
  });
  test("encodes sequenced events as SSE", async () => {
    const message = createRealtimeMessage({ type: "notice", room: "chat", data: { ok: true } });
    const response = realtimeSseResponse(
      new ReadableStream({
        start(controller) {
          controller.enqueue({ sequence: 7, message });
          controller.close();
        },
      }),
    );
    expect(response.headers.get("content-type")).toContain("text/event-stream");
    expect(await response.text()).toContain("id: 7\nevent: notice\ndata:");
  });
});
