const validator = require('validator');
const logger = require('../utils/logger');

// Sanitizar entrada para prevenir XSS
const sanitizeInput = (req, res, next) => {
  try {
    // Sanitizar body
    if (req.body) {
      req.body = sanitizeObject(req.body);
    }

    // Sanitizar query params
    if (req.query) {
      req.query = sanitizeObject(req.query);
    }

    // Sanitizar params
    if (req.params) {
      req.params = sanitizeObject(req.params);
    }

    next();
  } catch (error) {
    logger.error('Error en sanitización de entrada:', error);
    next(error);
  }
};

// Función recursiva para sanitizar objetos
const sanitizeObject = (obj) => {
  if (typeof obj !== 'object' || obj === null) {
    return sanitizeValue(obj);
  }

  if (Array.isArray(obj)) {
    return obj.map(item => sanitizeObject(item));
  }

  const sanitized = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      sanitized[key] = sanitizeObject(obj[key]);
    }
  }
  return sanitized;
};

// Sanitizar valores individuales
const sanitizeValue = (value) => {
  if (typeof value === 'string') {
    // Escapar HTML para prevenir XSS
    return validator.escape(value.trim());
  }
  return value;
};

// Validar y sanitizar SQL inputs (capa adicional a Sequelize)
const preventSQLInjection = (req, res, next) => {
  try {
    const dangerous = [
    /'\s*;--?/i,
    /'\s*--/i,
    /union.*select/i,
    /select.*from.*information_schema/i,
    /drop.*table/i,
    /xp_/i,
    /sp_/i,
    /1=1/i,
    /';.*--/i
  ];
  const check = (value) => {
    if (typeof value !== 'string') return false;
    return dangerous.some(pattern => pattern.test(value));
  };
  const scan = (obj) => {
    if (!obj || typeof obj !== 'object') return false;
    return Object.values(obj).some(val => {
      if (val === null || val === undefined) return false;
      if (typeof val === 'object') return scan(val);
      return check(String(val));
    });
  };
  // EXCLUÍMOS explícitamente los IDs numéricos de los params
  const safeParams = { ...req.params };
  if (safeParams.id && /^\d+$/.test(safeParams.id)) delete safeParams.id;
  if (safeParams.userId && /^\d+$/.test(safeParams.userId)) delete safeParams.userId;
  if (scan(req.body) || scan(req.query) || scan(safeParams)) {
    logger.warn(`[SQLi DETECTADO] IP: ${req.ip} | User-Agent: ${req.get('User-Agent')} | URL: ${req.originalUrl} | Body: ${JSON.stringify(req.body)}`);
    // Aquí puedes incluso mandar una alerta a Discord/Slack si quieres
  }
  next(); // SIEMPRE continúa, nunca bloquea
  } catch (error) {
    logger.error('Error en prevención de SQL Injection:', error);
    next(error);
  }
};

// Prevenir NoSQL Injection
const preventNoSQLInjection = (req, res, next) => {
  try {
    const dangerousOperators = [
      '$where', '$ne', '$gt', '$lt', '$regex', '$expr', 
      '$text', '$elemMatch', '$mod', '$in', '$nin'
    ];
    const hasDangerousOperator = (obj) => {
      if (!obj || typeof obj !== 'object') return false;
      for (const key in obj) {
        if (dangerousOperators.includes(key)) {
          return true;
        }
        if (typeof obj[key] === 'object' && hasDangerousOperator(obj[key])) {
          return true;
        }
      }
      return false;
    };
    // Solo escaneamos body y query (params nunca tienen operadores Mongo)
    if (hasDangerousOperator(req.body) || hasDangerousOperator(req.query)) {
      logger.warn(`[NoSQLi ALERTA] Posible intento detectado - IP: ${req.ip} - URL: ${req.originalUrl} - Body: ${JSON.stringify(req.body)}`);
      // Puedes mandar alerta a Slack/Discord aquí
      // PERO NO BLOQUEES AQUÍ
    }
    next(); // Siempre pasa
  } catch (error) {
    logger.error('Error en prevención de NoSQL Injection:', error);
    next(error);
  }
};

// Validar tipos de datos esperados
const validateDataTypes = (schema) => {
  return (req, res, next) => {
    try {
      const data = req.body;
      const errors = [];

      for (const field in schema) {
        if (schema.hasOwnProperty(field)) {
          const expectedType = schema[field].type;
          const required = schema[field].required;
          const value = data[field];

          // Verificar campos requeridos
          if (required && (value === undefined || value === null || value === '')) {
            errors.push(`El campo ${field} es requerido`);
            continue;
          }

          // Verificar tipos si el valor existe
          if (value !== undefined && value !== null && value !== '') {
            const actualType = typeof value;
            
            if (expectedType === 'email' && !validator.isEmail(value)) {
              errors.push(`El campo ${field} debe ser un email válido`);
            } else if (expectedType === 'url' && !validator.isURL(value)) {
              errors.push(`El campo ${field} debe ser una URL válida`);
            } else if (expectedType === 'number' && isNaN(value)) {
              errors.push(`El campo ${field} debe ser un número`);
            } else if (expectedType === 'boolean' && typeof value !== 'boolean') {
              errors.push(`El campo ${field} debe ser un booleano`);
            } else if (expectedType === 'string' && actualType !== 'string') {
              errors.push(`El campo ${field} debe ser una cadena de texto`);
            } else if (expectedType === 'date' && !validator.isISO8601(value)) {
              errors.push(`El campo ${field} debe ser una fecha válida`);
            }

            // Validar longitud mínima
            if (schema[field].minLength && value.length < schema[field].minLength) {
              errors.push(`El campo ${field} debe tener al menos ${schema[field].minLength} caracteres`);
            }

            // Validar longitud máxima
            if (schema[field].maxLength && value.length > schema[field].maxLength) {
              errors.push(`El campo ${field} no debe exceder ${schema[field].maxLength} caracteres`);
            }
          }
        }
      }

      if (errors.length > 0) {
        return res.status(400).json({
          success: false,
          message: 'Errores de validación',
          errors
        });
      }

      next();
    } catch (error) {
      logger.error('Error en validación de tipos de datos:', error);
      next(error);
    }
  };
};

// Prevenir ataques de Path Traversal
const preventPathTraversal = (req, res, next) => {
  try {
    const suspiciousPatterns = [
      /\.\./g,  // ..
      /\.\\/g,  // .\
      /\.\//g,  // ./
      /%2e%2e/gi, // URL encoded ..
      /%252e%252e/gi, // Double URL encoded ..
      /\\/g     // Backslash
    ];

    const checkValue = (value) => {
      if (typeof value === 'string') {
        for (const pattern of suspiciousPatterns) {
          if (pattern.test(value)) {
            return true;
          }
        }
      }
      return false;
    };

    const checkObject = (obj) => {
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          const value = obj[key];
          if (typeof value === 'object' && value !== null) {
            if (checkObject(value)) return true;
          } else if (checkValue(value)) {
            return true;
          }
        }
      }
      return false;
    };

    if (checkObject(req.body) || checkObject(req.query) || checkObject(req.params)) {
      logger.warn(`Posible intento de Path Traversal detectado - IP: ${req.ip} - Path: ${req.path}`);
      return res.status(400).json({
        success: false,
        message: 'Entrada inválida detectada'
      });
    }

    next();
  } catch (error) {
    logger.error('Error en prevención de Path Traversal:', error);
    next(error);
  }
};

// Limitar tamaño de payload para prevenir ataques DoS
const payloadSizeLimit = (maxSize = '10mb') => {
  return (req, res, next) => {
    const contentLength = req.headers['content-length'];
    
    if (contentLength) {
      const sizeInMB = parseInt(contentLength) / (1024 * 1024);
      const maxSizeInMB = parseInt(maxSize);
      
      if (sizeInMB > maxSizeInMB) {
        logger.warn(`Payload demasiado grande rechazado - IP: ${req.ip} - Size: ${sizeInMB}MB`);
        return res.status(413).json({
          success: false,
          message: `El tamaño del payload excede el límite de ${maxSize}`
        });
      }
    }
    
    next();
  };
};

// Validar Content-Type
const validateContentType = (allowedTypes = ['application/json']) => {
  return (req, res, next) => {
    if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
      const contentType = req.headers['content-type'];
      
      if (!contentType) {
        return res.status(400).json({
          success: false,
          message: 'Content-Type header es requerido'
        });
      }

      const isAllowed = allowedTypes.some(type => contentType.includes(type));
      
      if (!isAllowed) {
        logger.warn(`Content-Type no permitido - IP: ${req.ip} - Type: ${contentType}`);
        return res.status(415).json({
          success: false,
          message: `Content-Type no soportado. Tipos permitidos: ${allowedTypes.join(', ')}`
        });
      }
    }
    
    next();
  };
};

// Prevenir HTTP Parameter Pollution
const preventHPP = (req, res, next) => {
  try {
    // Verificar si hay parámetros duplicados en query
    for (const key in req.query) {
      if (Array.isArray(req.query[key]) && !['filter', 'sort', 'include'].includes(key)) {
        logger.warn(`Posible HTTP Parameter Pollution detectado - IP: ${req.ip} - Param: ${key}`);
        // Tomar solo el primer valor
        req.query[key] = req.query[key][0];
      }
    }
    next();
  } catch (error) {
    logger.error('Error en prevención de HPP:', error);
    next(error);
  }
};

module.exports = {
  sanitizeInput,
  preventSQLInjection,
  preventNoSQLInjection,
  validateDataTypes,
  preventPathTraversal,
  payloadSizeLimit,
  validateContentType,
  preventHPP
};