import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import { createHash } from 'crypto';
import type { Request } from 'express';
import { redis } from '../../config/redis';

/**
 * Generate a rate-limit key combining both IP AND target email (2026-04-12).
 *
 * Pure IP rate limits can be bypassed by rotating IPs (easy on mobile/VPN),
 * and pure email rate limits can be DoS'd by an attacker hitting every known
 * email from many IPs to lock legitimate users out. Combining both means:
 *   - a single IP can't hammer a single email beyond the limit
 *   - a single IP still can't spray many different emails beyond a wider cap
 * The email is hashed to keep it out of Redis in plaintext.
 */
function keyForEmailAndIp(prefix: string) {
  return (req: Request): string => {
    const email = typeof req.body?.email === 'string' ? req.body.email.toLowerCase().trim() : '';
    const emailHash = email ? createHash('sha256').update(email).digest('hex').slice(0, 16) : 'noemail';
    return `${prefix}:${req.ip}:${emailHash}`;
  };
}

/** 3 requests per hour per (IP, email) pair for resending verification emails */
export function createVerificationRateLimit() {
  return rateLimit({
    windowMs: 60 * 60 * 1000,
    max: 3,
    standardHeaders: true,
    legacyHeaders: false,
    keyGenerator: keyForEmailAndIp('verify'),
    store: new RedisStore({
      sendCommand: (command: string, ...args: string[]) => redis.call(command, ...args) as Promise<any>,
      prefix: 'rl:verify-resend:',
    }),
    message: {
      error: {
        message: 'Too many verification email requests, please try again later',
        code: 'VERIFICATION_RATE_LIMIT_EXCEEDED',
      },
    },
  });
}

/** 3 requests per hour per (IP, email) pair for password reset emails */
export function createResetRateLimit() {
  return rateLimit({
    windowMs: 60 * 60 * 1000,
    max: 3,
    standardHeaders: true,
    legacyHeaders: false,
    keyGenerator: keyForEmailAndIp('reset'),
    store: new RedisStore({
      sendCommand: (command: string, ...args: string[]) => redis.call(command, ...args) as Promise<any>,
      prefix: 'rl:password-reset:',
    }),
    message: {
      error: {
        message: 'Too many password reset requests, please try again later',
        code: 'RESET_RATE_LIMIT_EXCEEDED',
      },
    },
  });
}
