const { sendError } = require("../utils/http");

const buckets = new Map();

function pruneExpired(now) {
  for (const [key, value] of buckets.entries()) {
    if (value.resetAt <= now) {
      buckets.delete(key);
    }
  }
}

function getClientIp(req) {
  const forwardedFor = req.headers["x-forwarded-for"];
  if (typeof forwardedFor === "string" && forwardedFor.trim()) {
    return forwardedFor.split(",")[0].trim();
  }
  return req.ip || req.socket?.remoteAddress || "unknown";
}

function createRateLimit({ keyPrefix, windowMs, max, message, keyFn }) {
  return (req, res, next) => {
    const now = Date.now();

    if (buckets.size > 5000) {
      pruneExpired(now);
    }

    const suffix = typeof keyFn === "function" ? keyFn(req) : getClientIp(req);
    const key = `${keyPrefix}:${suffix || "unknown"}`;
    const existing = buckets.get(key);
    const bucket =
      !existing || existing.resetAt <= now
        ? { count: 0, resetAt: now + windowMs }
        : existing;

    bucket.count += 1;
    buckets.set(key, bucket);

    if (bucket.count > max) {
      const retryAfterSeconds = Math.max(
        1,
        Math.ceil((bucket.resetAt - now) / 1000)
      );
      res.setHeader("Retry-After", String(retryAfterSeconds));
      return sendError(
        res,
        429,
        message || "Too many requests. Please try again later."
      );
    }

    return next();
  };
}

module.exports = {
  createRateLimit,
};
