/**
 * Integration tests for rate limiting
 * Tests per-tenant rate limiting, Redis-backed rate limiting, and rate limit recovery
 */

import { describe, it, expect, beforeAll } from '@jest/globals';
import {
  initTestServer,
  authenticatedRequest,
  unauthenticatedRequest,
  waitForServer,
} from './helpers/test-server';

describe('Rate Limiting Integration', () => {
  beforeAll(async () => {
    initTestServer();
    const isReady = await waitForServer(30000);
    if (!isReady) {
      throw new Error('Test server is not ready');
    }

    // Check if rate limiting is enabled
    const rateLimitEnabled = process.env.RATE_LIMIT_ENABLED !== 'false';
    if (!rateLimitEnabled) {
      console.warn('⚠️  Rate limiting is not enabled. Some tests may be skipped.');
    }
  });

  describe('Per-Tenant Rate Limiting', () => {
    it('should rate limit based on API key', async () => {
      const rateLimitMax = parseInt(process.env.RATE_LIMIT_MAX || '60', 10);
      const requests: Promise<Response>[] = [];

      // Send requests up to the limit
      for (let i = 0; i < rateLimitMax + 5; i++) {
        requests.push(
          authenticatedRequest('/api/runs', {
            method: 'GET',
          })
        );
      }

      const responses = await Promise.all(requests);
      
      // Count successful vs rate-limited responses
      const successful = responses.filter(r => r.status === 200 || r.status === 201);
      const rateLimited = responses.filter(r => r.status === 429);

      // Should have some successful requests
      expect(successful.length).toBeGreaterThan(0);
      
      // Should have some rate-limited requests if limit is exceeded
      if (requests.length > rateLimitMax) {
        expect(rateLimited.length).toBeGreaterThan(0);
      }
    });

    it('should return rate limit error with retry-after header', async () => {
      // Send many requests to trigger rate limit
      const rateLimitMax = parseInt(process.env.RATE_LIMIT_MAX || '60', 10);
      
      // Send requests beyond limit
      let rateLimitedResponse: Response | null = null;
      for (let i = 0; i < rateLimitMax + 10; i++) {
        const response = await authenticatedRequest('/api/runs', {
          method: 'GET',
        });
        
        if (response.status === 429) {
          rateLimitedResponse = response;
          break;
        }
        
        // Small delay to avoid overwhelming
        await new Promise(resolve => setTimeout(resolve, 10));
      }

      if (rateLimitedResponse) {
        expect(rateLimitedResponse.status).toBe(429);
        
        const data = await rateLimitedResponse.json();
        expect(data).toHaveProperty('error', 'rate_limited');
        expect(data).toHaveProperty('retry_after_ms');
        expect(data).toHaveProperty('limit');
        expect(data).toHaveProperty('window_ms');
      }
    });
  });

  describe('Rate Limit Recovery', () => {
    it.skip('should allow requests after rate limit window expires', async () => {
      // This test would require waiting for the rate limit window to expire (60s)
      // Skipping in CI to avoid timeouts - would need a test Redis instance with short TTL
      // In practice, you'd set a short window for testing or use a test Redis instance
    });
  });

  describe('Rate Limit Bypass', () => {
    it('should not rate limit health endpoints', async () => {
      // Send many requests to health endpoint
      const requests: Promise<Response>[] = [];
      for (let i = 0; i < 100; i++) {
        requests.push(unauthenticatedRequest('/health'));
      }

      const responses = await Promise.all(requests);
      
      // All should succeed (health endpoints bypass rate limiting)
      const allSuccessful = responses.every(r => r.status === 200);
      expect(allSuccessful).toBe(true);
    });

    it('should not rate limit ready endpoint', async () => {
      const requests: Promise<Response>[] = [];
      for (let i = 0; i < 100; i++) {
        requests.push(unauthenticatedRequest('/ready'));
      }

      const responses = await Promise.all(requests);
      const allSuccessful = responses.every(r => r.status === 200);
      expect(allSuccessful).toBe(true);
    });
  });

  describe('Different Rate Limits for Different Routes', () => {
    it('should apply different limits to read vs write operations', async () => {
      // Read operations typically have higher limits
      const readResponse = await authenticatedRequest('/api/runs', {
        method: 'GET',
      });

      // Write operations typically have lower limits
      const writeResponse = await authenticatedRequest('/api/runs', {
        method: 'POST',
        body: JSON.stringify({
          name: 'Rate Limit Test',
          input: { test: 'rate-limit' },
        }),
      });

      // Both should work initially (before hitting rate limits)
      // Accept 200/201 for success or 429 if rate limited
      expect([200, 201, 429]).toContain(readResponse.status);
      expect([200, 201, 429]).toContain(writeResponse.status);
    });
  });
});

