import { describe, expect, it, vi } from "vitest";
import { MemoryOutboxStorage, Outbox } from "./outbox";

describe("Outbox", () => {
  it("persists before attempting to send, and removes on success", async () => {
    const storage = new MemoryOutboxStorage();
    const send = vi.fn().mockResolvedValue(undefined);
    const outbox = new Outbox(storage, send);

    await outbox.enqueue({ clientMsgId: "a", channelId: "c1", text: "hi", createdAt: new Date().toISOString() });

    expect(send).toHaveBeenCalledOnce();
    expect(await storage.load()).toHaveLength(0);
  });

  it("keeps a failed send persisted rather than dropping it", async () => {
    const storage = new MemoryOutboxStorage();
    const send = vi.fn().mockRejectedValue(new Error("network down"));
    const outbox = new Outbox(storage, send);

    await outbox.enqueue({ clientMsgId: "a", channelId: "c1", text: "hi", createdAt: new Date().toISOString() });

    const pending = await storage.load();
    expect(pending).toHaveLength(1);
    expect(pending[0]!.status).toBe("failed");
    expect(pending[0]!.attempts).toBe(1);
  });

  it("replay resends every persisted entry in creation order", async () => {
    const storage = new MemoryOutboxStorage();
    await storage.save({ clientMsgId: "b", channelId: "c1", text: "second", createdAt: "2024-01-02", attempts: 1, status: "failed" });
    await storage.save({ clientMsgId: "a", channelId: "c1", text: "first", createdAt: "2024-01-01", attempts: 1, status: "failed" });

    const order: string[] = [];
    const send = vi.fn().mockImplementation(async (entry) => {
      order.push(entry.clientMsgId);
    });
    const outbox = new Outbox(storage, send);

    await outbox.replay();

    expect(order).toEqual(["a", "b"]);
    expect(await storage.load()).toHaveLength(0);
  });
});
