import { FileUploadValidator } from './FileUploadValidator';
import { ClientSideValidator, clientSideValidator } from './ClientSideValidator';
import {
  XSSUploadValidationContext,
  XSSUploadValidationResult,
  ClientValidationDetails,
} from './types';

/**
 * Orchestrates client-side and server-side XSS validation for file uploads
 * Provides unified validation experience across all upload entry points
 */
export class XSSUploadValidator {
  /**
   * Validate file for upload with client-side checks
   * Runs immediately when user selects file
   * @param file - File to validate
   * @param context - Validation context with allowed types and constraints
   * @returns Validation result with specific error details
   */
  static async validateFileForUpload(
    file: File,
    context: XSSUploadValidationContext
  ): Promise<XSSUploadValidationResult> {
    const clientValidation: ClientValidationDetails = {
      passed: true,
    };

    // Validate file name for XSS patterns
    const fileNameValidation = FileUploadValidator.validateFileName(file);
    if (!fileNameValidation.isValid) {
      clientValidation.passed = false;
      clientValidation.fileName = {
        passed: false,
        error: fileNameValidation.error,
      };
      return {
        isValid: false,
        clientValidation,
        sanitizedFileName: this.getSanitizedFileName(file.name),
      };
    }
    clientValidation.fileName = { passed: true };

    // Validate file extension
    const extensionValidation = FileUploadValidator.validateFileExtension(
      file,
      context.allowedExtensions
    );
    if (!extensionValidation.isValid) {
      clientValidation.passed = false;
      clientValidation.extension = {
        passed: false,
        error: extensionValidation.error,
      };
      return {
        isValid: false,
        clientValidation,
        sanitizedFileName: this.getSanitizedFileName(file.name),
      };
    }
    clientValidation.extension = { passed: true };

    // Validate file size
    const sizeValidation = FileUploadValidator.validateFileSize(
      file,
      context.maxSizeMB
    );
    if (!sizeValidation.isValid) {
      clientValidation.passed = false;
      clientValidation.size = {
        passed: false,
        error: sizeValidation.error,
      };
      return {
        isValid: false,
        clientValidation,
        sanitizedFileName: this.getSanitizedFileName(file.name),
      };
    }
    clientValidation.size = { passed: true };

    // Validate MIME type using magic byte detection
    // This is more reliable than browser's file.type for Office documents
    const mimeValidation = await clientSideValidator.validateUpload(
      file,
      file.type,
      context.allowedMimeTypes || []
    );
    if (!mimeValidation.isValid) {
      clientValidation.passed = false;
      clientValidation.mimeType = {
        passed: false,
        error: mimeValidation.error,
      };
      return {
        isValid: false,
        clientValidation,
        sanitizedFileName: this.getSanitizedFileName(file.name),
      };
    }
    clientValidation.mimeType = { passed: true };

    // All validations passed
    return {
      isValid: true,
      clientValidation,
      sanitizedFileName: this.getSanitizedFileName(file.name),
    };
  }

  /**
   * Get sanitized file name for safe display in UI
   * Removes or replaces suspicious characters, removes null bytes, truncates to 255 chars
   * @param fileName - Original file name
   * @returns Sanitized file name safe for display
   */
  static getSanitizedFileName(fileName: string): string {
    return FileUploadValidator.sanitizeFileName(fileName);
  }

  /**
   * Validate multiple files for upload
   * @param files - Files to validate
   * @param context - Validation context
   * @returns Object with valid and invalid files
   */
  static async validateFilesForUpload(
    files: File[],
    context: XSSUploadValidationContext
  ): Promise<{
    valid: Array<{ file: File; result: XSSUploadValidationResult }>;
    invalid: Array<{ file: File; result: XSSUploadValidationResult }>;
  }> {
    const valid: Array<{ file: File; result: XSSUploadValidationResult }> = [];
    const invalid: Array<{ file: File; result: XSSUploadValidationResult }> = [];

    for (const file of files) {
      const result = await this.validateFileForUpload(file, context);
      if (result.isValid) {
        valid.push({ file, result });
      } else {
        invalid.push({ file, result });
      }
    }

    return { valid, invalid };
  }
}
