/**
 * Simple SQLite-backed token-bucket rate limiter. Survives restarts and works
 * with the single-process Passenger/next-start deployment. Server-only.
 */
import { getDb } from './db';

export interface RateResult {
  ok: boolean;
  retryAfter: number; // seconds until next token (0 when ok)
}

/**
 * @param bucket   unique key, e.g. `otp:${ip}` or `ai:${subscriberId}`
 * @param capacity max tokens (burst)
 * @param refillPerSec tokens added per second
 */
export function rateLimit(bucket: string, capacity: number, refillPerSec: number): RateResult {
  const db = getDb();
  const now = Date.now() / 1000;

  const row = db.prepare('SELECT tokens, updated_at FROM rate_limits WHERE bucket = ?').get(bucket) as
    | { tokens: number; updated_at: number }
    | undefined;

  let tokens = row ? Math.min(capacity, row.tokens + (now - row.updated_at) * refillPerSec) : capacity;

  if (tokens < 1) {
    const retryAfter = Math.ceil((1 - tokens) / refillPerSec);
    // persist the refilled (still <1) balance so the clock keeps advancing
    db.prepare(
      `INSERT INTO rate_limits (bucket, tokens, updated_at) VALUES (?, ?, ?)
       ON CONFLICT(bucket) DO UPDATE SET tokens = excluded.tokens, updated_at = excluded.updated_at`,
    ).run(bucket, tokens, now);
    return { ok: false, retryAfter };
  }

  tokens -= 1;
  db.prepare(
    `INSERT INTO rate_limits (bucket, tokens, updated_at) VALUES (?, ?, ?)
     ON CONFLICT(bucket) DO UPDATE SET tokens = excluded.tokens, updated_at = excluded.updated_at`,
  ).run(bucket, tokens, now);
  return { ok: true, retryAfter: 0 };
}

/** Best-effort client IP from request headers (behind cPanel/Passenger proxy). */
export function clientIp(req: Request): string {
  const xff = req.headers.get('x-forwarded-for');
  if (xff) return xff.split(',')[0].trim();
  return req.headers.get('x-real-ip') ?? 'unknown';
}
