import { describe, expect, it } from "vitest";
import { PendingSaveSyncRepository, SAVE_SYNC_RETRY_DELAYS_MS, SaveSyncRetryCoordinator } from "./saveSync";

function memoryStorage(initial = "[]") {
  const values = new Map([["i-want-to-heal:saves:pending-sync:v1", initial]]);
  return {
    getItem: (key: string) => values.get(key) ?? null,
    setItem: (key: string, value: string) => { values.set(key, value); },
  };
}

describe("pending save sync", () => {
  it("persists unique dirty slots and tolerates corrupt state", () => {
    const storage = memoryStorage();
    const pending = new PendingSaveSyncRepository(storage);
    pending.mark(2);
    pending.mark(2);
    pending.mark(1);
    expect(pending.list()).toEqual([2, 1]);
    pending.clear(2);
    expect(pending.list()).toEqual([1]);
    expect(new PendingSaveSyncRepository(memoryStorage("not-json")).list()).toEqual([]);
  });

  it("waits offline and caps automatic retries", async () => {
    const pending = new PendingSaveSyncRepository(memoryStorage());
    pending.mark(1);
    let online = false;
    const uploads: number[] = [];
    const tasks: Array<{ run: () => void; delay: number }> = [];
    const coordinator = new SaveSyncRetryCoordinator(
      pending,
      async (slotId) => { uploads.push(slotId); return false; },
      () => online,
      (run, delay) => {
        tasks.push({ run, delay });
        return tasks.length as unknown as ReturnType<typeof setTimeout>;
      },
      () => undefined,
    );

    coordinator.flush();
    expect(uploads).toEqual([]);
    online = true;
    coordinator.flush(true);
    await Promise.resolve();
    expect(uploads).toEqual([1]);

    for (const delay of SAVE_SYNC_RETRY_DELAYS_MS) {
      coordinator.failed(1);
      expect(tasks[0]?.delay).toBe(delay);
      tasks.shift()!.run();
      await Promise.resolve();
    }
    coordinator.failed(1);
    expect(tasks).toEqual([]);
    expect(uploads).toHaveLength(1 + SAVE_SYNC_RETRY_DELAYS_MS.length);
    coordinator.dispose();
  });
});
