// node --experimental-strip-types --test
import { test } from "node:test";
import assert from "node:assert/strict";
import { signToken, verifyToken, mintClaims } from "../src/relayToken.ts";

const SECRET = "test-secret-please-change";
const T0 = 1_767_322_245_000; // fixed "now" in ms

function conn(now = T0) {
  return mintClaims({ roomId: "room1", deviceId: "dev1", role: "client", typ: "c", ttlSeconds: 120, now });
}

test("sign + verify round-trips valid claims", async () => {
  const token = await signToken(SECRET, conn());
  const v = await verifyToken(SECRET, token, T0);
  assert.equal(v.ok, true);
  if (v.ok) {
    assert.equal(v.claims.roomId, "room1");
    assert.equal(v.claims.typ, "c");
    assert.equal(v.claims.role, "client");
  }
});

test("rejects a tampered payload (role escalation)", async () => {
  const claims = conn();
  const token = await signToken(SECRET, claims);
  const sig = token.split(".")[1];
  const forged = Buffer.from(JSON.stringify({ ...claims, role: "host" })).toString("base64url") + "." + sig;
  const v = await verifyToken(SECRET, forged, T0);
  assert.equal(v.ok, false);
});

test("rejects a wrong secret", async () => {
  const token = await signToken(SECRET, conn());
  const v = await verifyToken("other-secret", token, T0);
  assert.equal(v.ok, false);
});

test("rejects an expired token", async () => {
  const token = await signToken(SECRET, conn());
  const v = await verifyToken(SECRET, token, T0 + 121_000);
  assert.equal(v.ok, false);
  if (!v.ok) assert.equal(v.error, "expired");
});

test("rejects a malformed token", async () => {
  assert.equal((await verifyToken(SECRET, "garbage", T0)).ok, false);
  assert.equal((await verifyToken(SECRET, ".", T0)).ok, false);
});

test("mintClaims sets ttl from iat", () => {
  const m = mintClaims({ roomId: "r", deviceId: "d", role: "host", typ: "m", ttlSeconds: 100, now: 0 });
  assert.equal(m.exp - m.iat, 100);
  assert.equal(m.typ, "m");
  assert.equal(m.role, "host");
});
