import { expect, test } from "bun:test";
import {
  createPushSubscriptionService,
  memoryPushSubscriptionStore,
  postgresPushSubscriptionStore,
  renderOfflineQueueReview,
} from "../src/index.ts";

test("push subscriptions validate, deduplicate and persist per user", async () => {
  const service = createPushSubscriptionService(memoryPushSubscriptionStore(), {
    maxPerUser: 1,
    now: () => 10,
  });
  const value = { endpoint: "https://push.test/sub", keys: { p256dh: "public", auth: "secret" } };
  const first = await service.subscribe("user", value);
  expect((await service.list("user"))[0]).toEqual(first);
  expect((await service.subscribe("user", value)).id).toBe(first.id);
  await expect(
    service.subscribe("user", { ...value, endpoint: "https://push.test/other" }),
  ).rejects.toThrow("CAPACITY");
  await expect(
    service.subscribe("user", { ...value, endpoint: "http://push.test/insecure" }),
  ).rejects.toThrow("HTTPS");
});

test("PostgreSQL subscriptions parameterize endpoint and user", async () => {
  const calls: unknown[][] = [];
  const store = postgresPushSubscriptionStore({
    async query<T>(_sql: string, params?: unknown[]) {
      calls.push(params ?? []);
      return { rows: [] as T[] };
    },
  });
  await store.put({
    id: "id",
    userId: "user",
    endpoint: "https://push.test",
    keys: { p256dh: "p", auth: "a" },
    createdAt: 1,
  });
  expect(calls[0]?.slice(0, 3)).toEqual(["id", "user", "https://push.test"]);
});

test("offline review UI escapes payload-derived identifiers and conflicts", () => {
  const html = renderOfflineQueueReview(
    [
      {
        id: `"><script>`,
        endpoint: "/api/save",
        method: "POST",
        payload: {},
        attempts: 2,
        createdAt: 1,
        updatedAt: 1,
      },
    ],
    [{ id: "c", message: "<img onerror=alert(1)>" }],
  );
  expect(html).not.toContain("<script>");
  expect(html).not.toContain("<img");
  expect(html).toContain("data-pwa-retry");
});
