import { tap, expect } from '@git.zone/tstest/tapbundle';
import { RateLimiter } from '../ts/mail/delivery/classes.ratelimiter.js';

tap.test('RateLimiter - should be instantiable', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 10,
    periodMs: 1000,
    perKey: true
  });
  
  expect(limiter).toBeTruthy();
});

tap.test('RateLimiter - should allow requests within rate limit', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 5,
    periodMs: 1000,
    perKey: true
  });
  
  // Should allow 5 requests
  for (let i = 0; i < 5; i++) {
    expect(limiter.isAllowed('test')).toEqual(true);
  }
  
  // 6th request should be denied
  expect(limiter.isAllowed('test')).toEqual(false);
});

tap.test('RateLimiter - should enforce per-key limits', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 3,
    periodMs: 1000,
    perKey: true
  });
  
  // Should allow 3 requests for key1
  for (let i = 0; i < 3; i++) {
    expect(limiter.isAllowed('key1')).toEqual(true);
  }
  
  // 4th request for key1 should be denied
  expect(limiter.isAllowed('key1')).toEqual(false);
  
  // But key2 should still be allowed
  expect(limiter.isAllowed('key2')).toEqual(true);
});

tap.test('RateLimiter - should refill tokens over time', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 2,
    periodMs: 100, // Short period for testing
    perKey: true
  });
  
  // Use all tokens
  expect(limiter.isAllowed('test')).toEqual(true);
  expect(limiter.isAllowed('test')).toEqual(true);
  expect(limiter.isAllowed('test')).toEqual(false);
  
  // Wait for refill
  await new Promise(resolve => setTimeout(resolve, 150));
  
  // Should have tokens again
  expect(limiter.isAllowed('test')).toEqual(true);
});

tap.test('RateLimiter - should support burst allowance', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 2,
    periodMs: 100,
    perKey: true,
    burstTokens: 2, // Allow 2 extra tokens for bursts
    initialTokens: 4  // Start with max + burst tokens
  });
  
  // Should allow 4 requests (2 regular + 2 burst)
  for (let i = 0; i < 4; i++) {
    expect(limiter.isAllowed('test')).toEqual(true);
  }
  
  // 5th request should be denied
  expect(limiter.isAllowed('test')).toEqual(false);
  
  // Wait for refill
  await new Promise(resolve => setTimeout(resolve, 150));
  
  // Should have 2 tokens again (rate-limited to normal max, not burst)
  expect(limiter.isAllowed('test')).toEqual(true);
  expect(limiter.isAllowed('test')).toEqual(true);
  
  // 3rd request after refill should fail (only normal max is refilled, not burst)
  expect(limiter.isAllowed('test')).toEqual(false);
});

tap.test('RateLimiter - should return correct stats', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 10,
    periodMs: 1000,
    perKey: true
  });
  
  // Make some requests
  limiter.isAllowed('test');
  limiter.isAllowed('test');
  limiter.isAllowed('test');
  
  // Get stats
  const stats = limiter.getStats('test');
  
  expect(stats.remaining).toEqual(7);
  expect(stats.limit).toEqual(10);
  expect(stats.allowed).toEqual(3);
  expect(stats.denied).toEqual(0);
});

tap.test('RateLimiter - should reset limits', async () => {
  const limiter = new RateLimiter({
    maxPerPeriod: 3,
    periodMs: 1000,
    perKey: true
  });
  
  // Use all tokens
  expect(limiter.isAllowed('test')).toEqual(true);
  expect(limiter.isAllowed('test')).toEqual(true);
  expect(limiter.isAllowed('test')).toEqual(true);
  expect(limiter.isAllowed('test')).toEqual(false);
  
  // Reset
  limiter.reset('test');
  
  // Should have tokens again
  expect(limiter.isAllowed('test')).toEqual(true);
});

tap.test('stop', async () => {
  await tap.stopForcefully();
});

export default tap.start();