import { rateLimit, ipKeyGenerator, type RateLimitRequestHandler } from "express-rate-limit";
import type { Request, Response, NextFunction, RequestHandler } from "express";

const isProduction = process.env.NODE_ENV === "production";

function envInt(name: string, fallback: number): number {
  const raw = process.env[name];
  if (raw == null || raw === "") return fallback;
  const n = Number(raw);
  return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
}

function isLoopback(ip: string | undefined): boolean {
  if (!ip) return false;
  if (ip === "127.0.0.1" || ip === "::1") return true;
  if (ip.startsWith("::ffff:127.")) return true;
  return false;
}

function skipForLocalDev(req: Request): boolean {
  if (isProduction) return false;
  if (process.env.AUTH_RATE_LIMIT_FORCE === "1") return false;
  return isLoopback(req.ip);
}

function jsonHandler(req: Request, res: Response): void {
  res.status(429).json({
    error: "too_many_requests",
    message: "Too many attempts. Please slow down and try again later.",
  });
}

export const loginIpLimiter: RateLimitRequestHandler = rateLimit({
  windowMs: envInt("AUTH_RATE_LIMIT_LOGIN_WINDOW_MS", 60_000),
  max: envInt("AUTH_RATE_LIMIT_LOGIN_MAX", 10),
  standardHeaders: true,
  legacyHeaders: false,
  skip: skipForLocalDev,
  handler: jsonHandler,
});

export const registerIpLimiter: RateLimitRequestHandler = rateLimit({
  windowMs: envInt("AUTH_RATE_LIMIT_REGISTER_WINDOW_MS", 60 * 60_000),
  max: envInt("AUTH_RATE_LIMIT_REGISTER_MAX", 5),
  standardHeaders: true,
  legacyHeaders: false,
  skip: skipForLocalDev,
  handler: jsonHandler,
});

export const forgotPasswordIpLimiter: RateLimitRequestHandler = rateLimit({
  windowMs: envInt("AUTH_RATE_LIMIT_FORGOT_WINDOW_MS", 15 * 60_000),
  max: envInt("AUTH_RATE_LIMIT_FORGOT_MAX", 5),
  standardHeaders: true,
  legacyHeaders: false,
  skip: skipForLocalDev,
  handler: jsonHandler,
});

export const resetPasswordIpLimiter: RateLimitRequestHandler = rateLimit({
  windowMs: envInt("AUTH_RATE_LIMIT_RESET_WINDOW_MS", 15 * 60_000),
  max: envInt("AUTH_RATE_LIMIT_RESET_MAX", 10),
  standardHeaders: true,
  legacyHeaders: false,
  skip: skipForLocalDev,
  handler: jsonHandler,
});

export const loginUsernameLimiter: RateLimitRequestHandler = rateLimit({
  windowMs: envInt("AUTH_RATE_LIMIT_LOGIN_USERNAME_WINDOW_MS", 15 * 60_000),
  max: envInt("AUTH_RATE_LIMIT_LOGIN_USERNAME_MAX", 8),
  standardHeaders: true,
  legacyHeaders: false,
  skip: (req: Request) => {
    if (skipForLocalDev(req)) return true;
    const username =
      typeof req.body?.username === "string" ? req.body.username.trim() : "";
    return username.length === 0;
  },
  keyGenerator: (req: Request) => {
    const username =
      typeof req.body?.username === "string"
        ? req.body.username.trim().toLowerCase()
        : "";
    return `username:${username}`;
  },
  handler: jsonHandler,
});

function chain(...mws: RequestHandler[]): RequestHandler {
  return (req: Request, res: Response, next: NextFunction) => {
    let i = 0;
    const run = (err?: unknown): void => {
      if (err) return next(err as Error);
      const mw = mws[i++];
      if (!mw) return next();
      mw(req, res, run);
    };
    run();
  };
}

export const loginLimiters: RequestHandler = chain(
  loginIpLimiter,
  loginUsernameLimiter,
);
export const registerLimiters: RequestHandler = registerIpLimiter;
export const forgotPasswordLimiters: RequestHandler = forgotPasswordIpLimiter;
export const resetPasswordLimiters: RequestHandler = resetPasswordIpLimiter;

export { ipKeyGenerator };
