/**
 * Input Sanitization Utility
 * Provides methods to sanitize user inputs and prevent XSS/injection attacks
 */
export class InputSanitizer {
  // HTML entities to escape
  private static readonly HTML_ENTITIES: Record<string, string> = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    '/': '&#x2F;',
    '`': '&#x60;',
    '=': '&#x3D;',
  };

  // Patterns that indicate potential XSS attacks
  private static readonly XSS_PATTERNS: RegExp[] = [
    /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
    /javascript:/gi,
    /on\w+\s*=/gi,
    /data:/gi,
    /vbscript:/gi,
    /expression\s*\(/gi,
  ];

  // SQL injection patterns
  private static readonly SQL_PATTERNS: RegExp[] = [
    /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE|EXEC|UNION|DECLARE)\b)/gi,
    /('|"|;|--|\*|\/\*|\*\/)/g,
    /(\bOR\b|\bAND\b)\s+[\w'"]+\s*=\s*[\w'"]+/gi,
  ];

  /**
   * Escape HTML special characters to prevent XSS
   */
  static escapeHtml(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }
    return input.replace(/[&<>"'`=\/]/g, (char) => this.HTML_ENTITIES[char] || char);
  }

  /**
   * Remove all HTML tags from input
   */
  static stripHtml(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }
    return input.replace(/<[^>]*>/g, '');
  }

  /**
   * Detect and neutralize XSS attack patterns
   */
  static sanitizeForXss(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    let sanitized = input;

    // Remove potential XSS patterns
    for (const pattern of this.XSS_PATTERNS) {
      sanitized = sanitized.replace(pattern, '');
    }

    // Escape remaining HTML entities
    return this.escapeHtml(sanitized);
  }

  /**
   * Check if input contains potential SQL injection
   */
  static containsSqlInjection(input: string): boolean {
    if (!input || typeof input !== 'string') {
      return false;
    }

    for (const pattern of this.SQL_PATTERNS) {
      if (pattern.test(input)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Sanitize input to prevent SQL injection (for display purposes)
   * Note: Always use parameterized queries on backend - this is defense in depth
   */
  static sanitizeForSql(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    // Remove SQL keywords and special characters
    let sanitized = input;
    for (const pattern of this.SQL_PATTERNS) {
      sanitized = sanitized.replace(pattern, '');
    }
    return sanitized.trim();
  }

  /**
   * Sanitize email input
   */
  static sanitizeEmail(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    // Remove whitespace and convert to lowercase
    let email = input.trim().toLowerCase();

    // Remove any HTML/script tags
    email = this.stripHtml(email);

    // Remove dangerous characters, keeping only valid email characters
    email = email.replace(/[^a-z0-9@._+-]/g, '');

    // Validate basic email structure
    const emailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/;
    if (!emailRegex.test(email)) {
      return '';
    }

    return email;
  }

  /**
   * Sanitize phone number input
   */
  static sanitizePhone(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    // Remove all non-digit characters except + for country code
    let phone = input.replace(/[^\d+]/g, '');

    // Ensure + is only at the beginning
    if (phone.includes('+')) {
      const plusIndex = phone.indexOf('+');
      if (plusIndex > 0) {
        phone = phone.replace(/\+/g, '');
      } else {
        phone = '+' + phone.slice(1).replace(/\+/g, '');
      }
    }

    // Validate phone length (international format: 7-15 digits)
    const digitCount = phone.replace(/\D/g, '').length;
    if (digitCount < 7 || digitCount > 15) {
      return '';
    }

    return phone;
  }

  /**
   * Sanitize alphanumeric input (like department codes, IDs)
   */
  static sanitizeAlphanumeric(input: string, allowedChars: string = '_-'): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    // Create regex pattern with allowed characters
    const pattern = new RegExp(`[^a-zA-Z0-9${allowedChars.replace(/[-]/g, '\\-')}]`, 'g');

    return input.trim().replace(pattern, '').toUpperCase();
  }

  /**
   * Sanitize general text input
   */
  static sanitizeText(input: string, maxLength: number = 1000): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    let text = input.trim();

    // Truncate to max length
    if (text.length > maxLength) {
      text = text.substring(0, maxLength);
    }

    // Remove XSS patterns and escape HTML
    return this.sanitizeForXss(text);
  }

  /**
   * Sanitize name input (allows unicode letters, spaces, hyphens, apostrophes)
   */
  static sanitizeName(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    let name = input.trim();

    // Remove HTML tags first
    name = this.stripHtml(name);

    // Allow only letters (unicode), spaces, hyphens, and apostrophes
    // Remove numbers and special characters
    name = name.replace(/[0-9<>"'`;=&]/g, '');

    // Collapse multiple spaces
    name = name.replace(/\s+/g, ' ');

    // Limit length
    if (name.length > 100) {
      name = name.substring(0, 100);
    }

    return name;
  }

  /**
   * Validate and sanitize URL
   */
  static sanitizeUrl(input: string): string {
    if (!input || typeof input !== 'string') {
      return '';
    }

    let url = input.trim();

    // Check for javascript: or data: protocols
    const lowerUrl = url.toLowerCase();
    if (
      lowerUrl.startsWith('javascript:') ||
      lowerUrl.startsWith('data:') ||
      lowerUrl.startsWith('vbscript:')
    ) {
      return '';
    }

    // Only allow http, https protocols
    if (!lowerUrl.startsWith('http://') && !lowerUrl.startsWith('https://')) {
      // If no protocol, assume https
      url = 'https://' + url;
    }

    try {
      new URL(url);
      return url;
    } catch {
      return '';
    }
  }

  /**
   * Detect if string contains potentially malicious content
   */
  static isMalicious(input: string): boolean {
    if (!input || typeof input !== 'string') {
      return false;
    }

    // Check for XSS patterns
    for (const pattern of this.XSS_PATTERNS) {
      if (pattern.test(input)) {
        return true;
      }
    }

    // Check for SQL injection
    if (this.containsSqlInjection(input)) {
      return true;
    }

    return false;
  }
}
