import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
import { RedisRateLimiter } from '../middleware/spam-rate-limit.middleware';
import { redis } from '../config/redis';

describe('RedisRateLimiter', () => {
  const testKey = 'test-client';
  const limiter = new RedisRateLimiter();

  beforeAll(async () => {
    await redis.connect();
  });

  afterAll(async () => {
    await redis.quit();
  });

  beforeEach(async () => {
    await redis.del('spamshield:ratelimit:test-client');
    await redis.del('spamshield:ratelimit:daily:test-client');
  });

  afterEach(async () => {
    await redis.del('spamshield:ratelimit:test-client');
    await redis.del('spamshield:ratelimit:daily:test-client');
  });

  describe('checkLimit (per-minute)', () => {
    it('should allow requests within the limit', async () => {
      const result = await limiter.checkLimit(testKey, 60, 10);

      expect(result.remaining).toBe(9);
      expect(result.retryAfter).toBeUndefined();
    });

    it('should decrement remaining on each request', async () => {
      const result1 = await limiter.checkLimit(testKey, 60, 10);
      const result2 = await limiter.checkLimit(testKey, 60, 10);

      expect(result1.remaining).toBe(9);
      expect(result2.remaining).toBe(8);
    });

    it('should exceed limit after max requests', async () => {
      for (let i = 0; i < 10; i++) {
        await limiter.checkLimit(testKey, 60, 10);
      }

      const result = await limiter.checkLimit(testKey, 60, 10);

      expect(result.remaining).toBe(0);
      expect(result.retryAfter).toBeGreaterThan(0);
    });

    it('should return retry-after when limit is exceeded', async () => {
      for (let i = 0; i < 10; i++) {
        await limiter.checkLimit(testKey, 60, 10);
      }

      const result = await limiter.checkLimit(testKey, 60, 10);

      expect(result.retryAfter).toBeGreaterThan(0);
      expect(result.retryAfter).toBeLessThanOrEqual(60000);
    });
  });

  describe('checkDailyLimit', () => {
    it('should allow requests within daily limit', async () => {
      const result = await limiter.checkDailyLimit(testKey, 100);

      expect(result.remaining).toBe(99);
      expect(result.retryAfter).toBeUndefined();
    });

    it('should exceed daily limit after max requests', async () => {
      for (let i = 0; i < 100; i++) {
        await limiter.checkDailyLimit(testKey, 100);
      }

      const result = await limiter.checkDailyLimit(testKey, 100);

      expect(result.remaining).toBe(0);
      expect(result.retryAfter).toBeGreaterThan(0);
    });
  });

  describe('reset', () => {
    it('should clear the rate limit counter', async () => {
      await limiter.checkLimit(testKey, 60, 10);
      await limiter.checkLimit(testKey, 60, 10);

      await limiter.reset(testKey);

      const result = await limiter.checkLimit(testKey, 60, 10);

      expect(result.remaining).toBe(9);
    });
  });
});
