/**
 * SQL Injection Prevention
 * Provides comprehensive protection against SQL injection attacks
 */

import type { SqlInjectionPattern } from './types';

export class SqlInjectionPrevention {
  private static readonly SQL_INJECTION_PATTERNS: SqlInjectionPattern[] = [
    // Union-based injection patterns
    {
      pattern: /(\bunion\b.*\bselect\b)|(\bselect\b.*\bunion\b)/gi,
      severity: 'critical',
      description: 'UNION-based SQL injection attempt detected'
    },

    // Boolean-based blind injection
    {
      pattern: /(\b(and|or)\b\s*\d+\s*[=<>]\s*\d+)|(\b(and|or)\b\s*['"]?\w+['"]?\s*[=<>]\s*['"]?\w+['"]?)/gi,
      severity: 'high',
      description: 'Boolean-based blind SQL injection attempt detected'
    },

    // Time-based blind injection
    {
      pattern: /\b(sleep|waitfor|delay|benchmark)\s*\(/gi,
      severity: 'critical',
      description: 'Time-based blind SQL injection attempt detected'
    },

    // Error-based injection
    {
      pattern: /\b(extractvalue|updatexml|exp|floor|rand)\s*\(/gi,
      severity: 'high',
      description: 'Error-based SQL injection attempt detected'
    },

    // Stacked queries
    {
      pattern: /;\s*(drop|delete|insert|update|create|alter|exec|execute)\b/gi,
      severity: 'critical',
      description: 'Stacked query SQL injection attempt detected'
    },

    // Comment-based evasion
    {
      pattern: /(\/\*.*?\*\/)|(--.*)|(#.*)/g,
      severity: 'medium',
      description: 'SQL comment-based evasion attempt detected'
    },

    // Information schema access
    {
      pattern: /\binformation_schema\b/gi,
      severity: 'high',
      description: 'Information schema access attempt detected'
    },

    // System function calls
    {
      pattern: /\b(xp_cmdshell|sp_oacreate|sp_oamethod|openrowset|opendatasource)\b/gi,
      severity: 'critical',
      description: 'System function SQL injection attempt detected'
    },

    // Hex encoding evasion
    {
      pattern: /0x[0-9a-f]+/gi,
      severity: 'medium',
      description: 'Hex-encoded SQL injection attempt detected'
    },

    // Concatenation-based evasion
    {
      pattern: /\bconcat\s*\(|(\|\|)|(\+\s*['"])/gi,
      severity: 'medium',
      description: 'Concatenation-based SQL injection attempt detected'
    }
  ];

  /**
   * Validates input for SQL injection patterns
   */
  public static validateInput(input: string): { isValid: boolean; threats: SqlInjectionPattern[] } {
    if (typeof input !== 'string') {
      return { isValid: true, threats: [] };
    }

    const detectedThreats: SqlInjectionPattern[] = [];

    for (const pattern of this.SQL_INJECTION_PATTERNS) {
      if (pattern.pattern.test(input)) {
        detectedThreats.push(pattern);
      }
    }

    return {
      isValid: detectedThreats.length === 0,
      threats: detectedThreats
    };
  }

  /**
   * Sanitizes input by removing or escaping SQL injection patterns
   */
  public static sanitizeInput(input: string, options: { strict?: boolean } = {}): string {
    if (typeof input !== 'string') {
      return String(input);
    }

    let sanitized = input;

    if (options.strict) {
      // In strict mode, remove any detected patterns entirely
      for (const pattern of this.SQL_INJECTION_PATTERNS) {
        sanitized = sanitized.replace(pattern.pattern, '');
      }
    } else {
      // In normal mode, escape dangerous characters
      sanitized = sanitized
        .replace(/'/g, "''")  // Escape single quotes
        .replace(/"/g, '""')  // Escape double quotes
        .replace(/\\/g, '\\\\') // Escape backslashes
        .replace(/;/g, '\\;')   // Escape semicolons
        .replace(/--/g, '\\-\\-') // Escape SQL comments
        .replace(/\/\*/g, '\\/\\*') // Escape block comments
        .replace(/\*\//g, '\\*\\/'); // Escape block comments
    }

    return sanitized.trim();
  }

  /**
   * Creates a parameterized query-safe version of input
   */
  public static createSafeParameter(input: any): any {
    if (typeof input === 'string') {
      return this.sanitizeInput(input, { strict: true });
    }

    if (typeof input === 'number' || typeof input === 'boolean') {
      return input;
    }

    if (input === null || input === undefined) {
      return null;
    }

    if (Array.isArray(input)) {
      return input.map(item => this.createSafeParameter(item));
    }

    if (typeof input === 'object') {
      const safeObject: Record<string, any> = {};
      for (const [key, value] of Object.entries(input)) {
        safeObject[this.sanitizeInput(key)] = this.createSafeParameter(value);
      }
      return safeObject;
    }

    return String(input);
  }

  /**
   * Validates and sanitizes multiple inputs
   */
  public static validateAndSanitizeInputs(inputs: Record<string, any>): {
    isValid: boolean;
    threats: Array<{ field: string; threats: SqlInjectionPattern[] }>;
    sanitizedInputs: Record<string, any>;
  } {
    const allThreats: Array<{ field: string; threats: SqlInjectionPattern[] }> = [];
    const sanitizedInputs: Record<string, any> = {};

    for (const [field, value] of Object.entries(inputs)) {
      try {
        const sanitized = this.createSafeParameter(value);
        sanitizedInputs[field] = sanitized;

        if (typeof value === 'string') {
          const validation = this.validateInput(value);
          if (!validation.isValid) {
            allThreats.push({ field, threats: validation.threats });
          }
        }
      } catch (error) {
        allThreats.push({
          field,
          threats: [{
            pattern: /.*/,
            severity: 'critical',
            description: error instanceof Error ? error.message : 'Unknown SQL injection threat'
          }]
        });
      }
    }

    return {
      isValid: allThreats.length === 0,
      threats: allThreats,
      sanitizedInputs
    };
  }
}
