/**
 * Rate limiting middleware for Fastify
 * Uses identity-based rate limiting (API key, JWT sub, or IP fallback)
 */

import { FastifyRequest, FastifyReply } from 'fastify';
import { env } from '@4runr/shared';

/**
 * Get identity key for rate limiting
 * Priority: API key > JWT sub > JWT tenantId > IP address
 */
function getIdentityKey(request: FastifyRequest): string {
  // Try API key first
  const apiKey = (request as any).apiKey;
  if (apiKey) {
    return `apikey:${apiKey}`;
  }

  // Try JWT claims
  const auth = (request as any).auth || (request as any).user;
  if (auth) {
    if (auth.sub) {
      return `jwt:sub:${auth.sub}`;
    }
    if (auth.tenantId) {
      return `jwt:tenant:${auth.tenantId}`;
    }
  }

  // Fallback to IP address
  const ip = request.ip || request.headers['x-forwarded-for'] || request.headers['x-real-ip'] || 'unknown';
  return `ip:${ip}`;
}

type RateBucket = {
  count: number;
  resetAt: number;
};

const buckets = new Map<string, RateBucket>();

function createRateLimiter(max: number, windowMs: number) {
  return async function rateLimitMiddleware(
    request: FastifyRequest,
    reply: FastifyReply
  ): Promise<void> {
    if (!env.RATE_LIMIT_ENABLED) {
      return;
    }

    const key = getIdentityKey(request);
    const now = Date.now();

    let bucket = buckets.get(key);
    if (!bucket || now >= bucket.resetAt) {
      bucket = {
        count: 0,
        resetAt: now + windowMs
      };
      buckets.set(key, bucket);
    }

    bucket.count++;

    if (bucket.count > max) {
      const retryAfterMs = bucket.resetAt - now;
      reply.status(429).send({
        error: 'rate_limited',
        message: 'Too many requests, please try again later',
        retry_after_ms: retryAfterMs > 0 ? retryAfterMs : windowMs,
        limit: max,
        window_ms: windowMs,
      });
    }
  };
}

// Stricter limit (50% of global)
export const strictRateLimit = createRateLimiter(
  Math.floor(env.RATE_LIMIT_MAX / 2),
  env.RATE_LIMIT_WINDOW_MS
);

// Write operations (75% of global)
export const writeRateLimit = createRateLimiter(
  Math.floor(env.RATE_LIMIT_MAX * 0.75),
  env.RATE_LIMIT_WINDOW_MS
);

// Read operations (100% of global)
export const readRateLimit = createRateLimiter(
  env.RATE_LIMIT_MAX,
  env.RATE_LIMIT_WINDOW_MS
);

// No-op helper (for symmetry / future use)
export async function skipRateLimit(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  return;
}

