import {
  FileTypeDetectionResult,
  ValidationResult,
  UploadValidationOptions,
} from './types';
import { SVGContentValidator } from './SVGContentValidator';
import { EmbeddedContentValidator } from './EmbeddedContentValidator';

/**
 * Central utility for validating file buffers against declared MIME types
 * Uses magic byte detection to prevent XSS attacks via file type spoofing
 * 
 * NOTE: This validator is designed to run on the server/backend ONLY.
 * It requires the 'file-type' package which is Node.js-only.
 * 
 * For browser usage, use ClientSideValidator instead.
 */
export class BufferContentValidator {
  /**
   * Detect file type from buffer using magic bytes
   * For text-based files (SVG, HTML, XML), falls back to content analysis
   * NOTE: This method requires the 'file-type' package and only works on Node.js
   * @param buffer - File buffer to analyze
   * @returns Detection result with MIME type and extension, or null if unrecognized
   */
  async detectFileType(
    buffer: Buffer
  ): Promise<FileTypeDetectionResult | null> {
    try {
      if (!buffer || buffer.length === 0) {
        return null;
      }

      // Dynamically import file-type only on server-side
      // This prevents it from being bundled for the browser
      const { fileTypeFromBuffer } = await import('file-type');
      
      const detected = await fileTypeFromBuffer(buffer);
      if (detected) {
        return {
          mime: detected.mime,
          ext: detected.ext,
        };
      }

      // Fallback for text-based files without magic bytes (SVG, HTML, XML)
      const contentStr = buffer.toString('utf-8');
      
      // Check for SVG
      if (contentStr.includes('<svg') || contentStr.includes('<?xml') && contentStr.includes('<svg')) {
        return {
          mime: 'image/svg+xml',
          ext: 'svg',
        };
      }

      // Check for HTML
      if (contentStr.includes('<!DOCTYPE html') || contentStr.includes('<html')) {
        return {
          mime: 'text/html',
          ext: 'html',
        };
      }

      // Check for XML
      if (contentStr.includes('<?xml')) {
        return {
          mime: 'application/xml',
          ext: 'xml',
        };
      }

      return null;
    } catch (error) {
      console.error('Error detecting file type:', error);
      return null;
    }
  }

  /**
   * Validate declared MIME type against buffer content
   * @param buffer - File buffer to validate
   * @param declaredMime - MIME type declared by client
   * @returns Validation result with success status and details
   */
  async validateMimeType(
    buffer: Buffer,
    declaredMime: string
  ): Promise<ValidationResult> {
    try {
      if (!buffer || buffer.length === 0) {
        return {
          isValid: false,
          detectedMime: '',
          declaredMime,
          error: 'File buffer is empty or corrupted',
        };
      }

      const detected = await this.detectFileType(buffer);

      if (!detected) {
        return {
          isValid: false,
          detectedMime: '',
          declaredMime,
          error: 'Unable to detect real file type from buffer',
        };
      }

      const isValid = detected.mime === declaredMime;

      return {
        isValid,
        detectedMime: detected.mime,
        declaredMime,
        error: isValid
          ? undefined
          : `MIME mismatch. Declared: ${declaredMime}, Real: ${detected.mime}`,
      };
    } catch (error) {
      return {
        isValid: false,
        detectedMime: '',
        declaredMime,
        error: `Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`,
      };
    }
  }

  /**
   * Check if MIME type is in allowed list
   * @param mimeType - MIME type to check
   * @param allowedTypes - List of allowed MIME types
   * @returns True if MIME type is allowed
   */
  isAllowedMimeType(mimeType: string, allowedTypes: string[]): boolean {
    if (!allowedTypes || allowedTypes.length === 0) {
      return true;
    }
    return allowedTypes.includes(mimeType);
  }

  /**
   * Comprehensive validation: detect type, validate against declared, check whitelist, scan content
   * @param buffer - File buffer to validate
   * @param declaredMime - MIME type declared by client
   * @param options - Validation options including allowed types
   * @returns Validation result
   */
  async validateUpload(
    buffer: Buffer,
    declaredMime: string,
    options: UploadValidationOptions
  ): Promise<ValidationResult> {
    // First validate MIME type match
    const mimeValidation = await this.validateMimeType(buffer, declaredMime);

    if (!mimeValidation.isValid) {
      return mimeValidation;
    }

    // Then check if MIME type is allowed
    if (!this.isAllowedMimeType(declaredMime, options.allowedMimeTypes)) {
      return {
        isValid: false,
        detectedMime: mimeValidation.detectedMime,
        declaredMime,
        error: `File type ${declaredMime} is not allowed for this endpoint`,
      };
    }

    // Finally, scan file content for embedded XSS attacks
    const contentScan = this.scanContentForXSS(buffer, declaredMime);
    if (!contentScan.isValid) {
      return contentScan;
    }

    return mimeValidation;
  }

  /**
   * Validate SVG file content for XSS threats
   * @param buffer - SVG file buffer
   * @param fileName - File name for context
   * @returns Validation result
   */
  validateSVGContent(
    buffer: Buffer,
    fileName: string
  ): ValidationResult {
    const svgValidation = SVGContentValidator.validateSVGContent(buffer, fileName);

    if (!svgValidation.isValid) {
      return {
        isValid: false,
        detectedMime: 'image/svg+xml',
        declaredMime: 'image/svg+xml',
        error: svgValidation.error,
      };
    }

    return {
      isValid: true,
      detectedMime: 'image/svg+xml',
      declaredMime: 'image/svg+xml',
    };
  }

  /**
   * Scan file content for embedded XSS attacks
   * Checks actual file content regardless of file type
   * @param buffer - File buffer to scan
   * @param declaredMime - Declared MIME type
   * @returns Validation result
   */
  scanContentForXSS(
    buffer: Buffer,
    declaredMime: string
  ): ValidationResult {
    try {
      if (!buffer || buffer.length === 0) {
        return {
          isValid: true,
          detectedMime: declaredMime,
          declaredMime,
        };
      }

      // Check for embedded XSS patterns in file content
      const hasXSSPatterns = EmbeddedContentValidator.containsXSSPatterns(buffer, declaredMime);

      if (hasXSSPatterns) {
        const threatLevel = EmbeddedContentValidator.getThreatLevel(buffer, declaredMime);
        return {
          isValid: false,
          detectedMime: declaredMime,
          declaredMime,
          error: `File content contains potentially malicious code (threat level: ${threatLevel}). This file may contain embedded scripts or dangerous patterns.`,
        };
      }

      return {
        isValid: true,
        detectedMime: declaredMime,
        declaredMime,
      };
    } catch (error) {
      return {
        isValid: false,
        detectedMime: declaredMime,
        declaredMime,
        error: `Error scanning file content: ${error instanceof Error ? error.message : 'Unknown error'}`,
      };
    }
  }
}

// Export singleton instance
export const bufferContentValidator = new BufferContentValidator();
