/**
 * Brute force protection middleware
 * Tracks failed login attempts and temporarily blocks IPs with too many failures
 */

const logger = require("../config/logger");

// Store failed attempts in memory (use Redis in production)
const failedAttempts = new Map();
const blockedIPs = new Map();

// Configuration
const MAX_FAILED_ATTEMPTS = 5;
const BLOCK_DURATION = 15 * 60 * 1000; // 15 minutes
const ATTEMPT_WINDOW = 15 * 60 * 1000; // 15 minutes
const CLEANUP_INTERVAL = 60 * 1000; // 1 minute

/**
 * Clean up old entries periodically
 */
const cleanup = () => {
  const now = Date.now();

  // Clean up failed attempts
  for (const [ip, data] of failedAttempts.entries()) {
    if (now - data.firstAttempt > ATTEMPT_WINDOW) {
      failedAttempts.delete(ip);
    }
  }

  // Clean up blocked IPs
  for (const [ip, blockTime] of blockedIPs.entries()) {
    if (now - blockTime > BLOCK_DURATION) {
      blockedIPs.delete(ip);
      logger.info("IP unblocked after cooldown", { ip });
    }
  }
};

// Start cleanup interval
setInterval(cleanup, CLEANUP_INTERVAL);

/**
 * Record a failed login attempt
 * @param {string} ip - IP address
 */
const recordFailedAttempt = (ip) => {
  const now = Date.now();

  if (!failedAttempts.has(ip)) {
    failedAttempts.set(ip, {
      count: 1,
      firstAttempt: now,
    });
  } else {
    const data = failedAttempts.get(ip);

    // Reset if outside window
    if (now - data.firstAttempt > ATTEMPT_WINDOW) {
      data.count = 1;
      data.firstAttempt = now;
    } else {
      data.count++;
    }

    // Block if too many attempts
    if (data.count >= MAX_FAILED_ATTEMPTS) {
      blockedIPs.set(ip, now);
      logger.warn("IP blocked due to failed login attempts", {
        ip,
        attempts: data.count,
      });
    }
  }
};

/**
 * Reset failed attempts for an IP (on successful login)
 * @param {string} ip - IP address
 */
const resetFailedAttempts = (ip) => {
  failedAttempts.delete(ip);
};

/**
 * Check if an IP is currently blocked
 * @param {string} ip - IP address
 * @returns {boolean}
 */
const isBlocked = (ip) => {
  if (!blockedIPs.has(ip)) {
    return false;
  }

  const blockTime = blockedIPs.get(ip);
  const now = Date.now();

  // Check if block has expired
  if (now - blockTime > BLOCK_DURATION) {
    blockedIPs.delete(ip);
    return false;
  }

  return true;
};

/**
 * Get remaining block time in seconds
 * @param {string} ip - IP address
 * @returns {number} Seconds remaining
 */
const getRemainingBlockTime = (ip) => {
  if (!blockedIPs.has(ip)) {
    return 0;
  }

  const blockTime = blockedIPs.get(ip);
  const elapsed = Date.now() - blockTime;
  const remaining = Math.max(0, BLOCK_DURATION - elapsed);

  return Math.ceil(remaining / 1000);
};

/**
 * Middleware to check if IP is blocked
 */
const checkBlocked = (req, res, next) => {
  const ip = req.ip || req.connection.remoteAddress;

  if (isBlocked(ip)) {
    const remainingSeconds = getRemainingBlockTime(ip);
    logger.warn("Blocked IP attempted access", { ip, path: req.path });

    return res.status(429).json({
      success: false,
      message: `Too many failed attempts. Please try again in ${Math.ceil(
        remainingSeconds / 60
      )} minutes.`,
      retryAfter: remainingSeconds,
    });
  }

  next();
};

module.exports = {
  recordFailedAttempt,
  resetFailedAttempts,
  isBlocked,
  checkBlocked,
  getRemainingBlockTime,
};
