import assert from "node:assert/strict";
import test from "node:test";
import { RateLimitRepository, clientAddress, rateLimitResponse } from "../../lib/server/rate-limit.ts";
import { createTestD1 } from "../helpers/sqlite-d1.mjs";

test("allows a bounded fixed-window action and reports retry time", async () => {
  const { d1 } = await createTestD1();
  const limiter = new RateLimitRepository(d1, () => new Date("2026-08-13T06:00:30.000Z"));
  assert.deepEqual(await limiter.consume("upload", "203.0.113.1", { limit: 2, windowSeconds: 60 }), {
    allowed: true, remaining: 1, retryAfterSeconds: 0,
  });
  assert.equal((await limiter.consume("upload", "203.0.113.1", { limit: 2, windowSeconds: 60 })).allowed, true);
  const blocked = await limiter.consume("upload", "203.0.113.1", { limit: 2, windowSeconds: 60 });
  assert.equal(blocked.allowed, false);
  assert.equal(blocked.retryAfterSeconds, 30);
  assert.equal((await limiter.consume("upload", "203.0.113.2", { limit: 2, windowSeconds: 60 })).allowed, true);
});

test("starts a fresh count in the next window and prunes old rows", async () => {
  const { d1, database } = await createTestD1();
  let now = new Date("2026-08-13T06:00:59.000Z");
  const limiter = new RateLimitRepository(d1, () => now);
  await limiter.consume("audio", "ip", { limit: 1, windowSeconds: 60 });
  now = new Date("2026-08-13T06:01:00.000Z");
  assert.equal((await limiter.consume("audio", "ip", { limit: 1, windowSeconds: 60 })).allowed, true);
  now = new Date("2026-08-15T06:01:00.000Z");
  await limiter.consume("audio", "new", { limit: 1, windowSeconds: 60 });
  assert.equal(database.prepare("SELECT COUNT(*) count FROM rate_limits").get().count, 1);
});

test("uses Cloudflare's address and emits a standard 429 response", async () => {
  assert.equal(clientAddress(new Request("https://example.test", { headers: { "cf-connecting-ip": "203.0.113.9" } })), "203.0.113.9");
  assert.equal(clientAddress(new Request("https://example.test")), "unknown");
  const response = rateLimitResponse(37);
  assert.equal(response.status, 429);
  assert.equal(response.headers.get("retry-after"), "37");
  assert.match((await response.json()).error.message, /稍后/);
});
