import 'server-only';
import { createHmac } from 'node:crypto';
import { isIP } from 'node:net';
import { db } from './db';
import { getEnv } from './env';
import { AppError } from './errors';

export async function rateLimit(scope: string, subject: string, maximum: number, windowSeconds = 900) {
  const bucket = Math.floor(Date.now() / (windowSeconds * 1000));
  const key = createHmac('sha256', getEnv().RATE_LIMIT_SECRET).update(`${scope}:${subject}:${bucket}`).digest('hex');
  // Atomic DB increment works across processes. Expired rows are cleaned by the maintenance job.
  const entry = await db().rateLimitBucket.upsert({ where: { key },
    create: { key, count: 1, expiresAt: new Date((bucket + 1) * windowSeconds * 1000) },
    update: { count: { increment: 1 } },
  });
  if (entry.count > maximum) throw new AppError(429, 'RATE_LIMITED', 'Too many attempts. Please try again later.');
}

export function clientKey(request: Request) {
  if (getEnv().TRUST_CLOUDFLARE) {
    const ip = request.headers.get('cf-connecting-ip');
    if (!ip || !isIP(ip)) throw new AppError(400, 'INVALID_PROXY', 'Invalid request.');
    return ip;
  }
  // Do not trust X-Forwarded-For. Shared fallback is deliberately conservative.
  return 'untrusted-network';
}
