import { Context } from 'hono';
import { getRateLimiter } from './stores/rateLimiter.js';

/**
 * Rate limiting helpers.
 *
 * The actual counting is delegated to a pluggable rate limiter (in-memory by
 * default, Redis-backed when REDIS_URL is set) so limits are enforced either
 * per instance (single-instance deployments) or across all instances
 * (horizontal scaling). See lib/stores/rateLimiter.ts.
 */

/** Best-effort client IP extraction (honours common reverse-proxy headers). */
export function getClientIp(c: Context): string {
  const forwarded = c.req.header('x-forwarded-for');
  if (forwarded) return forwarded.split(',')[0].trim();
  return c.req.header('x-real-ip') || 'unknown';
}

/**
 * Consume one unit against a key. Returns whether the request is allowed and,
 * when blocked, how many seconds until the window resets.
 */
export function consumeRateLimit(
  key: string,
  max: number,
  windowMs: number
): Promise<{ allowed: boolean; retryAfter?: number }> {
  return getRateLimiter().consume(key, max, windowMs);
}

/**
 * Hono middleware factory that rate-limits by client IP.
 * Use a distinct `prefix` per endpoint group so unrelated routes don't share a bucket.
 */
export function rateLimitMiddleware(opts: { max: number; windowMs: number; prefix: string }) {
  return async (c: Context, next: () => Promise<void>) => {
    const ip = getClientIp(c);
    const result = await consumeRateLimit(`${opts.prefix}:${ip}`, opts.max, opts.windowMs);
    if (!result.allowed) {
      return c.json(
        { error: 'Too many requests. Please try again later.', retryAfter: result.retryAfter },
        429
      );
    }
    await next();
  };
}
