const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const xss = require('xss');
const crypto = require('crypto');

const sqlPatterns = [
  /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|TRUNCATE|ALTER|EXEC|EXECUTE|EXEC sp_|xp_)\b)/gi,
  /(union\s+select|union\s+all\s+select)/gi,
  /(--|#|\/\*|\*\/)/g,
  /(;|'|"|%27|%22|%3B)/g,
  /\b(or|and)\b\s+\d+\s*=\s*\d+/gi,
  /\b(or|and)\b\s+['"][^'"]*['"]\s*=\s*['"][^'"]*['"]/gi,
  /sleep\s*\(\s*\d+\s*\)/gi,
  /benchmark\s*\(/gi,
  /(1\s*=\s*1|2\s*=\s*2)/gi,
  /into\s+outfile/gi,
  /load_file\s*\(/gi
];

const xssPatterns = [
  /<script/i,
  /javascript:/i,
  /on\w+\s*=/i,
  /eval\s*\(/i,
  /expression\s*\(/i,
  /data:/i,
  /vbscript:/i,
  /<iframe/i,
  /<embed/i,
  /<object/i,
  /<applet/i
];

function sanitizeInput(input) {
  if (typeof input !== 'string') return input;
  
  let sanitized = input.trim();
  
  sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, '');
  
  return sanitized;
}

function stripSqlCommands(input) {
  if (typeof input !== 'string') return input;
  let stripped = input;
  
  for (const pattern of sqlPatterns) {
    stripped = stripped.replace(pattern, '');
  }
  
  return stripped;
}

function containsSqlInjection(input) {
  if (typeof input !== 'string') return false;
  
  const lowerInput = input.toLowerCase();
  
  for (const pattern of sqlPatterns) {
    const regex = new RegExp(pattern.source, pattern.flags);
    if (regex.test(lowerInput)) {
      return true;
    }
  }
  
  return false;
}

function containsXSS(input) {
  if (typeof input !== 'string') return false;
  
  for (const pattern of xssPatterns) {
    if (pattern.test(input)) {
      return true;
    }
  }
  
  return false;
}

const sqlInjectionLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 10,
  message: {
    success: false,
    message: 'Trop de requêtes suspectes. Veuillez patienter.',
    code: 'RATE_LIMIT_SQL'
  },
  standardHeaders: true,
  legacyHeaders: false,
  skip: (req) => {
    const suspiciousParams = ['q', 'query', 'search', 'term', 'input'];
    for (const param of suspiciousParams) {
      if (req.query[param] && containsSqlInjection(req.query[param])) {
        return false;
      }
    }
    return true;
  }
});

const generalLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 100,
  message: {
    success: false,
    message: 'Trop de requêtes. Veuillez patienter.',
    code: 'RATE_LIMIT'
  },
  standardHeaders: true,
  legacyHeaders: false
});

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 10,
  message: {
    success: false,
    message: 'Trop de tentatives de connexion. Veuillez patienter 15 minutes.',
    code: 'AUTH_LIMIT'
  },
  standardHeaders: true,
  legacyHeaders: false
});

const strictSqlLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 5,
  message: {
    success: false,
    message: 'Requête bloquée - injection SQL détectée.',
    code: 'SQL_INJECTION_DETECTED'
  },
  standardHeaders: true,
  legacyHeaders: false
});

function csrfProtection(req, res, next) {
  if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
    return next();
  }
  
  const csrfToken = req.headers['x-csrf-token'] || req.body._csrf;
  const sessionToken = req.session?.csrfToken;
  
  if (!sessionToken && req.method !== 'POST') {
    return next();
  }
  
  if (sessionToken && csrfToken !== sessionToken) {
    return res.status(403).json({
      success: false,
      message: 'Token CSRF invalide.',
      code: 'CSRF_INVALID'
    });
  }
  
  next();
}

function validateInput(req, res, next) {
  const suspiciousParams = ['q', 'query', 'search', 'term', 'input', 'question'];
  
  for (const param of suspiciousParams) {
    const value = req.body[param] || req.query[param];
    
    if (value) {
      if (containsSqlInjection(value)) {
        console.warn(`[SECURITY] SQL Injection détectée - Param: ${param}, Value: ${value.substring(0, 50)}`);
        return res.status(400).json({
          success: false,
          message: 'Requête invalide.',
          code: 'INVALID_INPUT'
        });
      }
      
      if (containsXSS(value)) {
        console.warn(`[SECURITY] XSS détecté - Param: ${param}, Value: ${value.substring(0, 50)}`);
        return res.status(400).json({
          success: false,
          message: 'Contenu invalide.',
          code: 'INVALID_CONTENT'
        });
      }
    }
  }
  
  next();
}

function sanitizeRequest(req, res, next) {
  if (req.body && typeof req.body === 'object') {
    for (const key in req.body) {
      if (typeof req.body[key] === 'string') {
        req.body[key] = sanitizeInput(req.body[key]);
      }
    }
  }
  
  next();
}

const securityHeaders = helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", "https:"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  },
  referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
  noSniff: true,
  xssFilter: true,
  hidePoweredBy: true
});

function generateCSRFToken() {
  return crypto.randomBytes(32).toString('hex');
}

module.exports = {
  sanitizeInput,
  stripSqlCommands,
  containsSqlInjection,
  containsXSS,
  sqlInjectionLimiter,
  generalLimiter,
  authLimiter,
  strictSqlLimiter,
  csrfProtection,
  validateInput,
  sanitizeRequest,
  securityHeaders,
  generateCSRFToken
};