/**
 * TASKMATE - Input Validierung
 * ============================
 * Schutz vor SQL-Injection und XSS
 */

const sanitizeHtml = require('sanitize-html');
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');

// DOMPurify für Server-side Rendering initialisieren
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);

/**
 * HTML-Entities dekodieren
 */
function decodeHtmlEntities(str) {
  if (typeof str !== 'string') return str;
  const entities = {
    '&amp;': '&',
    '&lt;': '<',
    '&gt;': '>',
    '&quot;': '"',
    '&#039;': "'",
    '&#x27;': "'",
    '&apos;': "'"
  };
  return str.replace(/&(amp|lt|gt|quot|#039|#x27|apos);/g, match => entities[match] || match);
}

/**
 * HTML-Tags entfernen (für reine Text-Felder)
 * Wichtig: sanitize-html encoded &-Zeichen zu &amp;, daher dekodieren wir danach
 */
function stripHtml(input) {
  if (typeof input !== 'string') return input;
  const sanitized = sanitizeHtml(input, {
    allowedTags: [],
    allowedAttributes: {}
  }).trim();
  // Entities wieder dekodieren, da sanitize-html sie encoded
  return decodeHtmlEntities(sanitized);
}

/**
 * Markdown-sichere Bereinigung mit DOMPurify (doppelte Sicherheit)
 */
function sanitizeMarkdown(input) {
  if (typeof input !== 'string') return input;
  
  // Erste Bereinigung mit sanitize-html
  const firstPass = sanitizeHtml(input, {
    allowedTags: [
      'p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre',
      'ul', 'ol', 'li', 'blockquote', 'a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
    ],
    allowedAttributes: {
      'a': ['href', 'title', 'target', 'rel']
    },
    allowedSchemes: ['http', 'https', 'mailto'],
    transformTags: {
      'a': (tagName, attribs) => {
        return {
          tagName: 'a',
          attribs: {
            ...attribs,
            target: '_blank',
            rel: 'noopener noreferrer'
          }
        };
      }
    }
  });

  // Zweite Bereinigung mit DOMPurify (zusätzliche Sicherheit)
  return DOMPurify.sanitize(firstPass, {
    ALLOWED_TAGS: [
      'p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre',
      'ul', 'ol', 'li', 'blockquote', 'a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
    ],
    ALLOWED_ATTR: ['href', 'title', 'target', 'rel'],
    ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i
  });
}

/**
 * Objekt rekursiv bereinigen
 */
function sanitizeObject(obj, options = {}) {
  if (obj === null || obj === undefined) return obj;

  if (typeof obj === 'string') {
    return options.allowHtml ? sanitizeMarkdown(obj) : stripHtml(obj);
  }

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

  if (typeof obj === 'object') {
    const sanitized = {};
    for (const [key, value] of Object.entries(obj)) {
      // Bestimmte Felder dürfen Markdown enthalten
      const allowHtml = ['description', 'content'].includes(key);
      
      // Passwort-Felder NICHT sanitizen (Sonderzeichen erhalten)
      const skipSanitization = ['password', 'oldPassword', 'newPassword', 'confirmPassword'].includes(key);
      
      if (skipSanitization) {
        sanitized[key] = value; // Passwort unverändert lassen
      } else {
        sanitized[key] = sanitizeObject(value, { allowHtml });
      }
    }
    return sanitized;
  }

  return obj;
}

/**
 * Validierungsfunktionen
 */
const validators = {
  /**
   * Pflichtfeld prüfen
   */
  required: (value, fieldName) => {
    if (value === undefined || value === null || value === '') {
      return `${fieldName} ist erforderlich`;
    }
    return null;
  },

  /**
   * Minimale Länge prüfen
   */
  minLength: (value, min, fieldName) => {
    if (typeof value === 'string' && value.length < min) {
      return `${fieldName} muss mindestens ${min} Zeichen haben`;
    }
    return null;
  },

  /**
   * Maximale Länge prüfen
   */
  maxLength: (value, max, fieldName) => {
    if (typeof value === 'string' && value.length > max) {
      return `${fieldName} darf maximal ${max} Zeichen haben`;
    }
    return null;
  },

  /**
   * E-Mail-Format prüfen
   */
  email: (value, fieldName) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (value && !emailRegex.test(value)) {
      return `${fieldName} muss eine gültige E-Mail-Adresse sein`;
    }
    return null;
  },

  /**
   * URL-Format prüfen (erweiterte Sicherheit)
   */
  url: (value, fieldName) => {
    try {
      if (value) {
        const url = new URL(value);
        
        // Nur HTTP/HTTPS erlauben
        if (!['http:', 'https:'].includes(url.protocol)) {
          return `${fieldName} muss HTTP oder HTTPS verwenden`;
        }
        
        // Localhost und private IPs blocken (SSRF-Schutz)
        const hostname = url.hostname;
        if (hostname === 'localhost' || 
            hostname === '127.0.0.1' || 
            hostname.startsWith('192.168.') ||
            hostname.startsWith('10.') ||
            hostname.startsWith('172.')) {
          return `${fieldName} darf nicht auf lokale Adressen verweisen`;
        }
        
        // JavaScript URLs blocken
        if (url.href.toLowerCase().startsWith('javascript:')) {
          return `${fieldName} enthält ungültigen JavaScript-Code`;
        }
      }
      return null;
    } catch {
      return `${fieldName} muss eine gültige URL sein`;
    }
  },

  /**
   * Integer prüfen
   */
  integer: (value, fieldName) => {
    if (value !== undefined && value !== null && !Number.isInteger(Number(value))) {
      return `${fieldName} muss eine ganze Zahl sein`;
    }
    return null;
  },

  /**
   * Positiver Integer prüfen
   */
  positiveInteger: (value, fieldName) => {
    const num = Number(value);
    if (value !== undefined && value !== null && (!Number.isInteger(num) || num < 0)) {
      return `${fieldName} muss eine positive ganze Zahl sein`;
    }
    return null;
  },

  /**
   * Datum prüfen (YYYY-MM-DD)
   */
  date: (value, fieldName) => {
    if (value) {
      const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
      if (!dateRegex.test(value)) {
        return `${fieldName} muss im Format YYYY-MM-DD sein`;
      }
      const date = new Date(value);
      if (isNaN(date.getTime())) {
        return `${fieldName} ist kein gültiges Datum`;
      }
    }
    return null;
  },

  /**
   * Enum-Wert prüfen
   */
  enum: (value, allowedValues, fieldName) => {
    if (value && !allowedValues.includes(value)) {
      return `${fieldName} muss einer der folgenden Werte sein: ${allowedValues.join(', ')}`;
    }
    return null;
  },

  /**
   * Hex-Farbe prüfen
   */
  hexColor: (value, fieldName) => {
    if (value) {
      const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
      if (!hexRegex.test(value)) {
        return `${fieldName} muss eine gültige Hex-Farbe sein (z.B. #FF0000)`;
      }
    }
    return null;
  }
};

/**
 * Passwort-Richtlinien prüfen
 */
function validatePassword(password) {
  const errors = [];
  const minLength = 8;

  if (!password || password.length < minLength) {
    errors.push(`Passwort muss mindestens ${minLength} Zeichen haben`);
  }

  if (password && !/[a-z]/.test(password)) {
    errors.push('Passwort muss mindestens einen Kleinbuchstaben enthalten');
  }

  if (password && !/[A-Z]/.test(password)) {
    errors.push('Passwort muss mindestens einen Großbuchstaben enthalten');
  }

  if (password && !/[0-9]/.test(password)) {
    errors.push('Passwort muss mindestens eine Zahl enthalten');
  }

  return errors;
}

/**
 * Express Middleware: Request-Body bereinigen
 */
function sanitizeMiddleware(req, res, next) {
  if (req.body && typeof req.body === 'object') {
    req.body = sanitizeObject(req.body);
  }

  if (req.query && typeof req.query === 'object') {
    req.query = sanitizeObject(req.query);
  }

  if (req.params && typeof req.params === 'object') {
    req.params = sanitizeObject(req.params);
  }

  next();
}

/**
 * Kontakt-Validierung Middleware
 */
validators.contact = function(req, res, next) {
  const errors = [];
  const { firstName, lastName, company, email, phone, mobile, website } = req.body;

  // Mindestens ein Name oder Firma muss vorhanden sein
  if (!firstName && !lastName && !company) {
    errors.push('Mindestens Vorname, Nachname oder Firma muss angegeben werden');
  }

  // Email validieren
  if (email) {
    const emailError = validators.email(email, 'E-Mail');
    if (emailError) errors.push(emailError);
  }

  // Website URL validieren
  if (website) {
    const urlError = validators.url(website, 'Website');
    if (urlError) errors.push(urlError);
  }

  // Telefonnummer Format (optional)
  if (phone && !/^[\d\s\-\+\(\)]+$/.test(phone)) {
    errors.push('Telefonnummer enthält ungültige Zeichen');
  }

  if (mobile && !/^[\d\s\-\+\(\)]+$/.test(mobile)) {
    errors.push('Mobilnummer enthält ungültige Zeichen');
  }

  if (errors.length > 0) {
    return res.status(400).json({ errors });
  }

  next();
};

module.exports = {
  stripHtml,
  sanitizeMarkdown,
  sanitizeObject,
  validators,
  validatePassword,
  sanitizeMiddleware
};
