/**
 * File Validation Service
 *
 * Validates uploaded files for security
 */

import { Injectable, Logger } from '@nestjs/common';
import { fileUploadConfig } from '../../infrastructure/security/security.config';
import * as path from 'path';

export interface FileValidationResult {
  valid: boolean;
  errors: string[];
}

@Injectable()
export class FileValidationService {
  private readonly logger = new Logger(FileValidationService.name);

  /**
   * Validate uploaded file
   */
  async validateFile(file: Express.Multer.File): Promise<FileValidationResult> {
    const errors: string[] = [];

    // Check if file exists
    if (!file) {
      errors.push('No file provided');
      return { valid: false, errors };
    }

    // Validate file size
    if (file.size > fileUploadConfig.maxFileSize) {
      errors.push(
        `File size exceeds maximum allowed size of ${fileUploadConfig.maxFileSize / 1024 / 1024}MB`
      );
    }

    // Validate MIME type
    if (!fileUploadConfig.allowedMimeTypes.includes(file.mimetype)) {
      errors.push(
        `File type ${
          file.mimetype
        } is not allowed. Allowed types: ${fileUploadConfig.allowedMimeTypes.join(', ')}`
      );
    }

    // Validate file extension
    const ext = path.extname(file.originalname).toLowerCase();
    if (!fileUploadConfig.allowedExtensions.includes(ext)) {
      errors.push(
        `File extension ${ext} is not allowed. Allowed extensions: ${fileUploadConfig.allowedExtensions.join(
          ', '
        )}`
      );
    }

    // Validate filename (prevent directory traversal)
    if (this.containsDirectoryTraversal(file.originalname)) {
      errors.push('Invalid filename: directory traversal detected');
    }

    // Check for executable files disguised with double extensions
    if (this.hasDoubleExtension(file.originalname)) {
      errors.push('Invalid filename: double extension detected');
    }

    // Validate file content matches extension (basic check)
    if (!this.contentMatchesExtension(file)) {
      errors.push('File content does not match extension');
    }

    const valid = errors.length === 0;

    if (!valid) {
      this.logger.warn(`File validation failed: ${errors.join(', ')}`);
    }

    return { valid, errors };
  }

  /**
   * Validate and sanitize filename
   */
  sanitizeFilename(filename: string): string {
    // Remove path traversal attempts
    let sanitized = path.basename(filename);

    // Remove special characters except dot, dash, underscore
    sanitized = sanitized.replace(/[^a-zA-Z0-9._-]/g, '_');

    // Limit filename length
    const ext = path.extname(sanitized);
    const name = path.basename(sanitized, ext);
    if (name.length > 100) {
      sanitized = name.substring(0, 100) + ext;
    }

    return sanitized;
  }

  /**
   * Check for directory traversal attempts
   */
  private containsDirectoryTraversal(filename: string): boolean {
    return (
      filename.includes('../') ||
      filename.includes('..\\') ||
      filename.includes('..\\') ||
      filename.includes('%2e%2e') ||
      filename.includes('0x2e0x2e')
    );
  }

  /**
   * Check for double extensions (e.g., file.pdf.exe)
   */
  private hasDoubleExtension(filename: string): boolean {
    const dangerousExtensions = [
      '.exe',
      '.bat',
      '.cmd',
      '.com',
      '.pif',
      '.scr',
      '.vbs',
      '.js',
      '.jar',
      '.msi',
      '.app',
      '.deb',
      '.rpm',
    ];

    const lowerFilename = filename.toLowerCase();
    return dangerousExtensions.some(ext => lowerFilename.includes(ext));
  }

  /**
   * Basic check if file content matches its extension
   */
  private contentMatchesExtension(file: Express.Multer.File): boolean {
    const ext = path.extname(file.originalname).toLowerCase();
    const buffer = file.buffer;

    if (!buffer || buffer.length < 4) {
      return false;
    }

    // Check file signatures (magic numbers)
    const signatures: Record<string, number[]> = {
      '.pdf': [0x25, 0x50, 0x44, 0x46], // %PDF
      '.jpg': [0xff, 0xd8, 0xff],
      '.jpeg': [0xff, 0xd8, 0xff],
      '.png': [0x89, 0x50, 0x4e, 0x47],
      '.webp': [0x52, 0x49, 0x46, 0x46], // RIFF (need to check WEBP later)
      '.xlsx': [0x50, 0x4b, 0x03, 0x04], // ZIP format
      '.xls': [0xd0, 0xcf, 0x11, 0xe0], // OLE2 format
    };

    const expectedSignature = signatures[ext];
    if (!expectedSignature) {
      // For unknown extensions, assume valid (CSV, etc.)
      return true;
    }

    // Check if buffer starts with expected signature
    for (let i = 0; i < expectedSignature.length; i++) {
      if (buffer[i] !== expectedSignature[i]) {
        return false;
      }
    }

    return true;
  }

  /**
   * Scan file for viruses (placeholder for production virus scanning)
   */
  async scanForViruses(file: Express.Multer.File): Promise<boolean> {
    if (!fileUploadConfig.scanForViruses) {
      return true; // Skip in development
    }

    // TODO: Integrate with ClamAV or similar virus scanner
    // For now, just log
    this.logger.log(`Virus scan requested for file: ${file.originalname} (not implemented)`);

    return true;
  }

  /**
   * Validate multiple files
   */
  async validateFiles(files: Express.Multer.File[]): Promise<FileValidationResult> {
    const allErrors: string[] = [];

    for (const file of files) {
      const result = await this.validateFile(file);
      if (!result.valid) {
        allErrors.push(...result.errors);
      }
    }

    return {
      valid: allErrors.length === 0,
      errors: allErrors,
    };
  }
}
