import { FileUploadValidator } from '../FileUploadValidator';

describe('FileUploadValidator', () => {
  describe('validateFileSize', () => {
    it('should accept files within size limit', () => {
      const file = new File(['content'], 'test.txt', { type: 'text/plain' });
      Object.defineProperty(file, 'size', { value: 1024 * 1024 }); // 1MB

      const result = FileUploadValidator.validateFileSize(file, 50);
      expect(result.isValid).toBe(true);
    });

    it('should reject files exceeding size limit', () => {
      const file = new File(['content'], 'test.txt', { type: 'text/plain' });
      Object.defineProperty(file, 'size', { value: 100 * 1024 * 1024 }); // 100MB

      const result = FileUploadValidator.validateFileSize(file, 50);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('exceeds maximum allowed size');
    });
  });

  describe('validateFileExtension', () => {
    it('should reject dangerous extensions', () => {
      const file = new File(['content'], 'malware.exe', { type: 'application/octet-stream' });

      const result = FileUploadValidator.validateFileExtension(file);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('not allowed for security reasons');
    });

    it('should accept allowed extensions', () => {
      const file = new File(['content'], 'image.jpg', { type: 'image/jpeg' });

      const result = FileUploadValidator.validateFileExtension(file, ['jpg', 'png']);
      expect(result.isValid).toBe(true);
    });

    it('should reject disallowed extensions', () => {
      const file = new File(['content'], 'document.exe', { type: 'application/octet-stream' });

      const result = FileUploadValidator.validateFileExtension(file, ['jpg', 'png']);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('not allowed');
    });

    it('should handle extensions with or without dot', () => {
      const file = new File(['content'], 'image.jpg', { type: 'image/jpeg' });

      const result1 = FileUploadValidator.validateFileExtension(file, ['.jpg', '.png']);
      const result2 = FileUploadValidator.validateFileExtension(file, ['jpg', 'png']);

      expect(result1.isValid).toBe(true);
      expect(result2.isValid).toBe(true);
    });
  });

  describe('validateMimeType', () => {
    it('should accept allowed MIME types', () => {
      const file = new File(['content'], 'image.jpg', { type: 'image/jpeg' });

      const result = FileUploadValidator.validateMimeType(file, ['image/jpeg', 'image/png']);
      expect(result.isValid).toBe(true);
    });

    it('should reject disallowed MIME types', () => {
      const file = new File(['content'], 'script.js', { type: 'application/javascript' });

      const result = FileUploadValidator.validateMimeType(file, ['image/jpeg', 'image/png']);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('not allowed');
    });

    it('should support wildcard MIME types', () => {
      const file = new File(['content'], 'image.jpg', { type: 'image/jpeg' });

      const result = FileUploadValidator.validateMimeType(file, ['image/*']);
      expect(result.isValid).toBe(true);
    });

    it('should accept any MIME type if list is empty', () => {
      const file = new File(['content'], 'anything.bin', { type: 'application/octet-stream' });

      const result = FileUploadValidator.validateMimeType(file, []);
      expect(result.isValid).toBe(true);
    });
  });

  describe('validateFileName', () => {
    it('should reject file names with invalid characters', () => {
      const file = new File(['content'], 'file<script>.txt', { type: 'text/plain' });

      const result = FileUploadValidator.validateFileName(file);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('suspicious characters');
    });

    it('should reject file names with javascript protocol', () => {
      const file = new File(['content'], 'javascript:alert(1).txt', { type: 'text/plain' });

      const result = FileUploadValidator.validateFileName(file);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('suspicious');
    });

    it('should reject file names with event handlers', () => {
      const file = new File(['content'], 'onerror=alert(1).txt', { type: 'text/plain' });

      const result = FileUploadValidator.validateFileName(file);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('suspicious');
    });

    it('should reject file names with null bytes', () => {
      const file = new File(['content'], 'file\0.txt', { type: 'text/plain' });

      const result = FileUploadValidator.validateFileName(file);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('null bytes');
    });

    it('should reject excessively long file names', () => {
      const longName = 'a'.repeat(300) + '.txt';
      const file = new File(['content'], longName, { type: 'text/plain' });

      const result = FileUploadValidator.validateFileName(file);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('too long');
    });

    it('should accept valid file names', () => {
      const file = new File(['content'], 'valid-file_name.txt', { type: 'text/plain' });

      const result = FileUploadValidator.validateFileName(file);
      expect(result.isValid).toBe(true);
    });
  });

  describe('validateFile', () => {
    it('should perform comprehensive validation', () => {
      const file = new File(['content'], 'image.jpg', { type: 'image/jpeg' });
      Object.defineProperty(file, 'size', { value: 1024 * 1024 }); // 1MB

      const result = FileUploadValidator.validateFile(file, {
        maxSizeMB: 50,
        allowedMimeTypes: ['image/jpeg', 'image/png'],
        allowedExtensions: ['jpg', 'png']
      });

      expect(result.isValid).toBe(true);
    });

    it('should reject file if any validation fails', () => {
      const file = new File(['content'], 'image.exe', { type: 'image/jpeg' });
      Object.defineProperty(file, 'size', { value: 1024 * 1024 }); // 1MB

      const result = FileUploadValidator.validateFile(file, {
        maxSizeMB: 50,
        allowedMimeTypes: ['image/jpeg'],
        allowedExtensions: ['jpg', 'png']
      });

      expect(result.isValid).toBe(false);
      expect(result.error).toContain('not allowed for security reasons');
    });

    it('should reject null file', () => {
      const result = FileUploadValidator.validateFile(null as any);
      expect(result.isValid).toBe(false);
      expect(result.error).toContain('No file provided');
    });
  });

  describe('validateFiles', () => {
    it('should validate multiple files', () => {
      const files = [
        new File(['content'], 'image1.jpg', { type: 'image/jpeg' }),
        new File(['content'], 'image2.png', { type: 'image/png' }),
      ];

      const result = FileUploadValidator.validateFiles(files, {
        allowedMimeTypes: ['image/jpeg', 'image/png']
      });

      expect(result.valid.length).toBe(2);
      expect(result.invalid.length).toBe(0);
    });

    it('should separate valid and invalid files', () => {
      const files = [
        new File(['content'], 'image.jpg', { type: 'image/jpeg' }),
        new File(['content'], 'script.js', { type: 'application/javascript' }),
      ];

      const result = FileUploadValidator.validateFiles(files, {
        allowedMimeTypes: ['image/jpeg', 'image/png']
      });

      expect(result.valid.length).toBe(1);
      expect(result.invalid.length).toBe(1);
      expect(result.invalid[0].file.name).toBe('script.js');
    });
  });

  describe('getFileCategory', () => {
    it('should categorize image files', () => {
      const file = new File(['content'], 'image.jpg', { type: 'image/jpeg' });
      expect(FileUploadValidator.getFileCategory(file)).toBe('image');
    });

    it('should categorize document files', () => {
      const file = new File(['content'], 'doc.pdf', { type: 'application/pdf' });
      expect(FileUploadValidator.getFileCategory(file)).toBe('document');
    });

    it('should categorize archive files', () => {
      const file = new File(['content'], 'archive.zip', { type: 'application/zip' });
      expect(FileUploadValidator.getFileCategory(file)).toBe('archive');
    });

    it('should categorize unknown files as other', () => {
      const file = new File(['content'], 'unknown.bin', { type: 'application/octet-stream' });
      expect(FileUploadValidator.getFileCategory(file)).toBe('other');
    });
  });

  describe('sanitizeFileName', () => {
    it('should remove invalid characters', () => {
      const result = FileUploadValidator.sanitizeFileName('file<script>.txt');
      expect(result).toBe('file_script_.txt');
    });

    it('should remove null bytes', () => {
      const result = FileUploadValidator.sanitizeFileName('file\0.txt');
      expect(result).toBe('file.txt');
    });

    it('should truncate long names', () => {
      const longName = 'a'.repeat(300) + '.txt';
      const result = FileUploadValidator.sanitizeFileName(longName);
      expect(result.length).toBe(255);
    });

    it('should preserve valid names', () => {
      const result = FileUploadValidator.sanitizeFileName('valid-file_name.txt');
      expect(result).toBe('valid-file_name.txt');
    });
  });
});
