import { describe, it, expect } from 'vitest';
import {
  enforceAdminRateLimit,
  enforcePublicCommentRateLimit,
  enforcePublicFormRateLimit,
  type AdminLimiterEnv,
  type RateLimiter,
} from './rate-limit.js';

function fakeLimiter(success: boolean, calls: { key: string }[] = []): RateLimiter {
  return {
    async limit(options: { key: string }) {
      calls.push(options);
      return { success };
    },
  };
}

const req = (path: string, method = 'GET', headers: Record<string, string> = {}) =>
  new Request(`https://mod-cms.test${path}`, { method, headers });

describe('enforceAdminRateLimit', () => {
  it('ignores non-admin paths — returns null and never calls a limiter', async () => {
    const calls: { key: string }[] = [];
    const env: AdminLimiterEnv = { ADMIN_API_LIMITER: fakeLimiter(true, calls) };
    expect(await enforceAdminRateLimit(env, req('/post/hello'))).toBeNull();
    expect(calls).toHaveLength(0);
  });

  it('allows an admin request that is under budget (null)', async () => {
    const env: AdminLimiterEnv = { ADMIN_API_LIMITER: fakeLimiter(true) };
    expect(await enforceAdminRateLimit(env, req('/api/admin/content'))).toBeNull();
  });

  it('returns 429 + Retry-After + rate_limited body when over budget', async () => {
    const env: AdminLimiterEnv = { ADMIN_API_LIMITER: fakeLimiter(false) };
    const res = await enforceAdminRateLimit(env, req('/api/admin/content'));
    expect(res?.status).toBe(429);
    expect(res?.headers.get('Retry-After')).toBe('60');
    expect(await res?.json()).toEqual({ error: { code: 'rate_limited', message: expect.any(String) } });
  });

  it('routes login POST to the tight login limiter, keyed login:<ip>', async () => {
    const loginCalls: { key: string }[] = [];
    const apiCalls: { key: string }[] = [];
    const env: AdminLimiterEnv = {
      ADMIN_LOGIN_LIMITER: fakeLimiter(true, loginCalls),
      ADMIN_API_LIMITER: fakeLimiter(true, apiCalls),
    };
    await enforceAdminRateLimit(env, req('/api/admin/session', 'POST', { 'CF-Connecting-IP': '1.2.3.4' }));
    expect(loginCalls).toEqual([{ key: 'login:1.2.3.4' }]);
    expect(apiCalls).toHaveLength(0);
  });

  it('routes non-login admin to the api limiter, keyed api:<ip>', async () => {
    const apiCalls: { key: string }[] = [];
    const env: AdminLimiterEnv = { ADMIN_API_LIMITER: fakeLimiter(true, apiCalls) };
    await enforceAdminRateLimit(env, req('/api/admin/content', 'GET', { 'CF-Connecting-IP': '9.9.9.9' }));
    expect(apiCalls).toEqual([{ key: 'api:9.9.9.9' }]);
  });

  it('GET /api/admin/session uses the api limiter, not login (login is POST-only)', async () => {
    const loginCalls: { key: string }[] = [];
    const apiCalls: { key: string }[] = [];
    const env: AdminLimiterEnv = {
      ADMIN_LOGIN_LIMITER: fakeLimiter(true, loginCalls),
      ADMIN_API_LIMITER: fakeLimiter(true, apiCalls),
    };
    await enforceAdminRateLimit(env, req('/api/admin/session', 'GET'));
    expect(loginCalls).toHaveLength(0);
    expect(apiCalls).toHaveLength(1);
  });

  it('falls back to api:unknown when CF-Connecting-IP is absent', async () => {
    const apiCalls: { key: string }[] = [];
    const env: AdminLimiterEnv = { ADMIN_API_LIMITER: fakeLimiter(true, apiCalls) };
    await enforceAdminRateLimit(env, req('/api/admin/content'));
    expect(apiCalls).toEqual([{ key: 'api:unknown' }]);
  });

  it('proceeds (null) when the binding is absent — local dev fail-open', async () => {
    expect(await enforceAdminRateLimit({}, req('/api/admin/content'))).toBeNull();
  });
});

describe('enforcePublicCommentRateLimit', () => {
  it('returns 429 + Retry-After when over budget', async () => {
    const res = await enforcePublicCommentRateLimit(
      { COMMENTS_LIMITER: fakeLimiter(false) },
      req('/api/comments', 'POST', { 'CF-Connecting-IP': '5.6.7.8' }),
    );
    expect(res?.status).toBe(429);
    expect(res?.headers.get('Retry-After')).toBe('60');
  });

  it('keys comment:<ip>', async () => {
    const calls: { key: string }[] = [];
    await enforcePublicCommentRateLimit(
      { COMMENTS_LIMITER: fakeLimiter(true, calls) },
      req('/api/comments', 'POST', { 'CF-Connecting-IP': '9.9.9.9' }),
    );
    expect(calls).toEqual([{ key: 'comment:9.9.9.9' }]);
  });

  it('fail-open when binding absent', async () => {
    expect(await enforcePublicCommentRateLimit({}, req('/api/comments', 'POST'))).toBeNull();
  });
});

describe('enforcePublicFormRateLimit', () => {
  it('returns 429 + Retry-After when over budget', async () => {
    const res = await enforcePublicFormRateLimit(
      { FORMS_LIMITER: fakeLimiter(false) },
      req('/api/contact', 'POST', { 'CF-Connecting-IP': '5.6.7.8' }),
    );
    expect(res?.status).toBe(429);
    expect(res?.headers.get('Retry-After')).toBe('60');
  });

  it('keys form:<ip>', async () => {
    const calls: { key: string }[] = [];
    await enforcePublicFormRateLimit(
      { FORMS_LIMITER: fakeLimiter(true, calls) },
      req('/api/contact', 'POST', { 'CF-Connecting-IP': '9.9.9.9' }),
    );
    expect(calls).toEqual([{ key: 'form:9.9.9.9' }]);
  });

  it('fail-open when binding absent', async () => {
    expect(await enforcePublicFormRateLimit({}, req('/api/contact', 'POST'))).toBeNull();
  });
});
