import { describe, it, expect } from 'vitest';
import { rateLimit, clientIpFromHeaders } from '@/lib/rateLimit';

describe('rateLimit', () => {
  it('allows requests under the limit', () => {
    const key = `test-allow-${Math.random()}`;
    for (let i = 0; i < 5; i++) {
      const r = rateLimit(key, { limit: 5, windowMs: 60_000 });
      expect(r.ok).toBe(true);
    }
  });

  it('blocks requests over the limit and reports retryAfterSec > 0', () => {
    const key = `test-block-${Math.random()}`;
    for (let i = 0; i < 3; i++) {
      rateLimit(key, { limit: 3, windowMs: 60_000 });
    }
    const r = rateLimit(key, { limit: 3, windowMs: 60_000 });
    expect(r.ok).toBe(false);
    if (!r.ok) expect(r.retryAfterSec).toBeGreaterThan(0);
  });

  it('isolates buckets by key', () => {
    const a = `iso-a-${Math.random()}`;
    const b = `iso-b-${Math.random()}`;
    for (let i = 0; i < 2; i++) {
      rateLimit(a, { limit: 2, windowMs: 60_000 });
    }
    expect(rateLimit(a, { limit: 2, windowMs: 60_000 }).ok).toBe(false);
    expect(rateLimit(b, { limit: 2, windowMs: 60_000 }).ok).toBe(true);
  });

  it('lets requests in again after the window slides', async () => {
    const key = `slide-${Math.random()}`;
    rateLimit(key, { limit: 1, windowMs: 50 });
    expect(rateLimit(key, { limit: 1, windowMs: 50 }).ok).toBe(false);
    await new Promise((r) => setTimeout(r, 60));
    expect(rateLimit(key, { limit: 1, windowMs: 50 }).ok).toBe(true);
  });
});

describe('clientIpFromHeaders', () => {
  it('uses the leftmost x-forwarded-for entry', () => {
    const h = new Headers({ 'x-forwarded-for': '1.2.3.4, 5.6.7.8' });
    expect(clientIpFromHeaders(h)).toBe('1.2.3.4');
  });

  it('falls back to x-real-ip', () => {
    const h = new Headers({ 'x-real-ip': '9.9.9.9' });
    expect(clientIpFromHeaders(h)).toBe('9.9.9.9');
  });

  it('returns "unknown" when no headers present', () => {
    expect(clientIpFromHeaders(new Headers())).toBe('unknown');
  });
});
