// node --experimental-strip-types --test
//
// The relay-presence → pool wake hook (docs/ANTIMATTER_RUNNER.md §3, §0.2 item 3e): host
// heartbeats register room→pool; the relay's authenticated hook resolves a roomId and forwards
// a pool-auth /wake.
import { test } from "node:test";
import assert from "node:assert/strict";
import { handleWakeHook, registerRoomMapping, ROOM_KEY_TTL, type KVLike } from "../src/wake.ts";

class FakeKV implements KVLike {
  store = new Map<string, string>();
  puts: Array<{ key: string; value: string; ttl?: number }> = [];
  async get(key: string): Promise<string | null> {
    return this.store.get(key) ?? null;
  }
  async put(key: string, value: string, opts?: { expirationTtl?: number }): Promise<void> {
    this.store.set(key, value);
    this.puts.push({ key, value, ttl: opts?.expirationTtl });
  }
}

const ROOM = "f".repeat(64);

test("host heartbeat registers the room→pool mapping with a refreshing TTL", async () => {
  const kv = new FakeKV();
  await registerRoomMapping(kv, "pool-1", "host", { roomId: ROOM, epoch: 1 });
  assert.equal(kv.store.get(`room:${ROOM}`), "pool-1");
  assert.equal(kv.puts[0]?.ttl, ROOM_KEY_TTL);
});

test("only the host slot may claim a room", async () => {
  const kv = new FakeKV();
  await registerRoomMapping(kv, "pool-1", "sb-1", { roomId: ROOM });
  assert.equal(kv.store.size, 0);
});

test("a malformed roomId is dropped, not stored", async () => {
  const kv = new FakeKV();
  await registerRoomMapping(kv, "pool-1", "host", { roomId: "not-hex" });
  await registerRoomMapping(kv, "pool-1", "host", { roomId: 42 });
  await registerRoomMapping(kv, "pool-1", "host", null);
  assert.equal(kv.store.size, 0);
});

function hookOpts(kv: FakeKV, overrides: Partial<Parameters<typeof handleWakeHook>[0]> = {}) {
  return {
    kv,
    secret: "wake-secret",
    token: "wake-secret",
    body: { roomId: ROOM } as Record<string, unknown> | null,
    forward: async (poolId: string) =>
      new Response(JSON.stringify({ woke: poolId }), { status: 200 }),
    ...overrides,
  };
}

test("wake hook resolves the room and forwards a pool wake", async () => {
  const kv = new FakeKV();
  kv.store.set(`room:${ROOM}`, "pool-1");
  const res = await handleWakeHook(hookOpts(kv));
  assert.equal(res.status, 200);
  assert.deepEqual(await res.json(), { woke: "pool-1" });
});

test("wake hook is disabled (501) when no secret is configured", async () => {
  const res = await handleWakeHook(hookOpts(new FakeKV(), { secret: undefined }));
  assert.equal(res.status, 501);
});

test("wake hook rejects a wrong or missing bearer", async () => {
  const kv = new FakeKV();
  kv.store.set(`room:${ROOM}`, "pool-1");
  assert.equal((await handleWakeHook(hookOpts(kv, { token: "wrong" }))).status, 401);
  assert.equal((await handleWakeHook(hookOpts(kv, { token: null }))).status, 401);
});

test("wake hook 404s an unregistered room and 422s a malformed one", async () => {
  const kv = new FakeKV();
  assert.equal((await handleWakeHook(hookOpts(kv))).status, 404);
  assert.equal((await handleWakeHook(hookOpts(kv, { body: { roomId: "nope" } }))).status, 422);
  assert.equal((await handleWakeHook(hookOpts(kv, { body: null }))).status, 422);
});

test("wake hook refuses a revoked pool", async () => {
  const kv = new FakeKV();
  kv.store.set(`room:${ROOM}`, "pool-1");
  kv.store.set("revoked:pool-1", "1");
  assert.equal((await handleWakeHook(hookOpts(kv))).status, 401);
});
