import { test } from 'node:test';
import assert from 'node:assert/strict';

const { rateLimit } = await import('../src/ratelimit.js');

function fakeCtx() {
  const headers = {};
  return {
    req: { header: () => undefined },
    header: (k, v) => { headers[k] = v; },
    json: (body, status = 200) => ({ __status: status, body }),
    _headers: headers,
  };
}

test('rateLimit: blockt nach Überschreiten mit 429 + Retry-After', async () => {
  const mw = rateLimit({ max: 2, windowMs: 10_000, keyFn: () => 'fix' });
  let calls = 0;
  const run = async () => { const c = fakeCtx(); const r = await mw(c, async () => { calls++; }); return { c, r }; };

  assert.equal((await run()).r, undefined);   // 1 → durch (next, kein Return)
  assert.equal((await run()).r, undefined);   // 2 → durch
  const { c, r } = await run();               // 3 → blockiert
  assert.equal(r.__status, 429);
  assert.ok(c._headers['Retry-After']);
  assert.equal(calls, 2);                      // next nur zweimal aufgerufen
});

test('rateLimit: getrennte Schlüssel zählen getrennt', async () => {
  let key = 'a';
  const mw = rateLimit({ max: 1, windowMs: 10_000, keyFn: () => key });
  assert.equal((await mw(fakeCtx(), async () => {})), undefined);      // a:1 ok
  assert.equal((await mw(fakeCtx(), async () => {})).__status, 429);   // a:2 blockiert
  key = 'b';
  assert.equal((await mw(fakeCtx(), async () => {})), undefined);      // b:1 ok
});

test('rateLimit: Fenster läuft ab → wieder frei', async () => {
  const mw = rateLimit({ max: 1, windowMs: 30, keyFn: () => 'win' });
  assert.equal((await mw(fakeCtx(), async () => {})), undefined);
  assert.equal((await mw(fakeCtx(), async () => {})).__status, 429);
  await new Promise((r) => setTimeout(r, 40));
  assert.equal((await mw(fakeCtx(), async () => {})), undefined);      // Fenster neu
});
