// Integration: Session 32 mounted the existing rate-limit middleware on the
// public cached routers. These tests prove the wiring at the ROUTER level —
// the middleware's own behavior (429, window reset, per-IP keys) is covered
// in tests/unit/rateLimitMiddleware.test.js.

const express = require('express');
const request = require('supertest');

// Mock redis so any handler that does run never touches a real server.
jest.mock('../../src/utils/redis', () => ({
  cacheGet: jest.fn(async () => null),
  cacheSet: jest.fn(async () => {}),
  getRedisClient: () => ({ get: async () => null, set: async () => 'OK', scan: async () => ['0', []], hgetall: async () => ({}) }),
  isDegraded: () => false,
}));

// Fresh module registry → fresh per-router bucket (createRateLimit state is
// module-scoped). jest.resetModules() resets Jest's registry; deleting
// require.cache alone does NOT under Jest.
function mountFresh(routePath, file) {
  jest.resetModules();
  const app = express();
  app.use(express.json());
  app.use(routePath, require(file));
  return app;
}

// A deep nonsense path enters the router (running router.use(rateLimit))
// but matches no route → 404. Lets us exhaust the limiter without invoking
// the real handlers or any upstream call.
const MISS = '/__rl__/__miss__/__deep__';

describe('public route rate limiting (Session 32 wiring)', () => {
  test('schedule router 429s after exceeding its 60/min limit', async () => {
    const app = mountFresh('/api/schedule', '../../src/routes/schedule');
    // 60 allowed (each 404s but counts), 61st is throttled.
    for (let i = 0; i < 60; i += 1) {
      const r = await request(app).get(`/api/schedule${MISS}`);
      expect(r.status).not.toBe(429);
    }
    const blocked = await request(app).get(`/api/schedule${MISS}`);
    expect(blocked.status).toBe(429);
    expect(blocked.body.error).toMatch(/too many/i);
  });

  test('odds router 429s after exceeding its tighter 30/min limit', async () => {
    const app = mountFresh('/api/odds', '../../src/routes/odds');
    for (let i = 0; i < 30; i += 1) {
      const r = await request(app).get(`/api/odds${MISS}`);
      expect(r.status).not.toBe(429);
    }
    expect((await request(app).get(`/api/odds${MISS}`)).status).toBe(429);
  });

  test('different route files have INDEPENDENT buckets (not a shared limit)', async () => {
    // Mount two routers in one app; exhaust parlay, confirm odds is unaffected.
    jest.resetModules();
    const app = express();
    app.use(express.json());
    app.use('/api/parlay', require('../../src/routes/parlay'));
    app.use('/api/odds', require('../../src/routes/odds'));

    // Exhaust parlay (30/min).
    for (let i = 0; i < 30; i += 1) await request(app).get(`/api/parlay${MISS}`);
    expect((await request(app).get(`/api/parlay${MISS}`)).status).toBe(429);

    // odds shares the same client IP but its own bucket — still open.
    expect((await request(app).get(`/api/odds${MISS}`)).status).not.toBe(429);
  });
});
