import {
  validateFileSize,
  validateFileType,
  getAttachmentType,
  validateFile,
  formatFileSize,
  MAX_FILE_SIZE,
  ALLOWED_EXTENSIONS
} from '../attachmentValidation';

describe('attachmentValidation', () => {
  describe('validateFileSize', () => {
    test('accepts file under limit', () => {
      const file = new File(['content'], 'test.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 5 * 1024 * 1024 }); // 5 MB
      const result = validateFileSize(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('rejects file over limit', () => {
      const file = new File(['content'], 'large.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 15 * 1024 * 1024 }); // 15 MB
      const result = validateFileSize(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('10 MB');
    });

    test('accepts file exactly at limit', () => {
      const file = new File(['content'], 'exact.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 10 * 1024 * 1024 }); // Exactly 10 MB
      const result = validateFileSize(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('rejects file exactly over limit by 1 byte', () => {
      const file = new File(['content'], 'oversize.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 10 * 1024 * 1024 + 1 }); // 10 MB + 1 byte
      const result = validateFileSize(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('10 MB');
    });

    test('accepts custom max size', () => {
      const file = new File(['content'], 'test.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 8 * 1024 * 1024 }); // 8 MB
      const result = validateFileSize(file, 5 * 1024 * 1024); // 5 MB limit
      expect(result.valid).toBe(false);
      // Note: Error message is hardcoded to "10 MB" per REQ-5.1, not dynamic
      expect(result.error).toContain('10 MB');
    });
  });

  describe('validateFileType', () => {
    test('accepts pdf file', () => {
      const file = new File(['content'], 'document.pdf', { type: 'application/pdf' });
      const result = validateFileType(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('accepts mp3 file', () => {
      const file = new File(['content'], 'music.mp3', { type: 'audio/mpeg' });
      const result = validateFileType(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('accepts wav file', () => {
      const file = new File(['content'], 'track.wav', { type: 'audio/wav' });
      const result = validateFileType(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('accepts png file', () => {
      const file = new File(['content'], 'image.png', { type: 'image/png' });
      const result = validateFileType(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('rejects exe file', () => {
      const file = new File(['content'], 'virus.exe', { type: 'application/x-msdownload' });
      const result = validateFileType(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('not supported');
      expect(result.error).toContain('.pdf');
    });

    test('rejects zip file', () => {
      const file = new File(['content'], 'archive.zip', { type: 'application/zip' });
      const result = validateFileType(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('not supported');
    });

    test('accepts uppercase extension', () => {
      const file = new File(['content'], 'DOCUMENT.PDF', { type: 'application/pdf' });
      const result = validateFileType(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('accepts mixed case extension', () => {
      const file = new File(['content'], 'Document.Pdf', { type: 'application/pdf' });
      const result = validateFileType(file);
      expect(result).toEqual({ valid: true, error: null });
    });

    test('accepts all allowed extensions', () => {
      const extensions = ['.pdf', '.xml', '.mxl', '.txt', '.png', '.jpg', '.jpeg', '.gif', '.mp3', '.wav'];
      extensions.forEach(ext => {
        const file = new File(['content'], `test${ext}`, { type: 'application/octet-stream' });
        const result = validateFileType(file);
        expect(result.valid).toBe(true);
      });
    });
  });

  describe('getAttachmentType', () => {
    test('identifies mp3 as audio', () => {
      expect(getAttachmentType('song.mp3')).toBe('audio');
    });

    test('identifies wav as audio', () => {
      expect(getAttachmentType('track.wav')).toBe('audio');
    });

    test('identifies flac as audio', () => {
      expect(getAttachmentType('audio.flac')).toBe('audio');
    });

    test('identifies ogg as audio', () => {
      expect(getAttachmentType('music.ogg')).toBe('audio');
    });

    test('identifies pdf as notation', () => {
      expect(getAttachmentType('sheet.pdf')).toBe('notation');
    });

    test('identifies xml as notation', () => {
      expect(getAttachmentType('score.xml')).toBe('notation');
    });

    test('identifies png as notation', () => {
      expect(getAttachmentType('image.png')).toBe('notation');
    });

    test('identifies txt as notation', () => {
      expect(getAttachmentType('notes.txt')).toBe('notation');
    });

    test('handles uppercase extensions', () => {
      expect(getAttachmentType('SONG.MP3')).toBe('audio');
      expect(getAttachmentType('SHEET.PDF')).toBe('notation');
    });

    test('handles multiple dots in filename', () => {
      expect(getAttachmentType('my.song.name.mp3')).toBe('audio');
      expect(getAttachmentType('document.v2.pdf')).toBe('notation');
    });
  });

  describe('validateFile', () => {
    test('accepts valid pdf file', () => {
      const file = new File(['content'], 'document.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 5 * 1024 * 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(true);
      expect(result.error).toBeNull();
      expect(result.warnings).toEqual([]);
    });

    test('rejects file over size limit', () => {
      const file = new File(['content'], 'large.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 15 * 1024 * 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('10 MB');
      expect(result.warnings).toEqual([]);
    });

    test('rejects invalid file type', () => {
      const file = new File(['content'], 'virus.exe', { type: 'application/x-msdownload' });
      Object.defineProperty(file, 'size', { value: 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('not supported');
      expect(result.warnings).toEqual([]);
    });

    test('warns about mp3 files (not backend supported)', () => {
      const file = new File(['content'], 'music.mp3', { type: 'audio/mpeg' });
      Object.defineProperty(file, 'size', { value: 5 * 1024 * 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(true);
      expect(result.error).toBeNull();
      expect(result.warnings).toHaveLength(1);
      expect(result.warnings[0]).toContain('.mp3');
      expect(result.warnings[0]).toContain('may not be supported');
    });

    test('does not warn about wav files (backend supported)', () => {
      const file = new File(['content'], 'track.wav', { type: 'audio/wav' });
      Object.defineProperty(file, 'size', { value: 5 * 1024 * 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(true);
      expect(result.error).toBeNull();
      expect(result.warnings).toEqual([]);
    });

    test('does not warn about pdf files (backend supported)', () => {
      const file = new File(['content'], 'document.pdf', { type: 'application/pdf' });
      Object.defineProperty(file, 'size', { value: 5 * 1024 * 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(true);
      expect(result.error).toBeNull();
      expect(result.warnings).toEqual([]);
    });

    test('stops at first error (does not check type if size fails)', () => {
      const file = new File(['content'], 'large.exe', { type: 'application/x-msdownload' });
      Object.defineProperty(file, 'size', { value: 15 * 1024 * 1024 });
      const result = validateFile(file);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('10 MB'); // Size error, not type error
    });
  });

  describe('formatFileSize', () => {
    test('formats bytes', () => {
      expect(formatFileSize(0)).toBe('0 B');
      expect(formatFileSize(500)).toBe('500 B');
      expect(formatFileSize(1023)).toBe('1023 B');
    });

    test('formats kilobytes', () => {
      expect(formatFileSize(1024)).toBe('1.0 KB');
      expect(formatFileSize(2048)).toBe('2.0 KB');
      expect(formatFileSize(1536)).toBe('1.5 KB');
      expect(formatFileSize(10240)).toBe('10.0 KB');
    });

    test('formats megabytes', () => {
      expect(formatFileSize(1024 * 1024)).toBe('1.0 MB');
      expect(formatFileSize(5 * 1024 * 1024)).toBe('5.0 MB');
      expect(formatFileSize(5242880)).toBe('5.0 MB');
      expect(formatFileSize(10 * 1024 * 1024)).toBe('10.0 MB');
    });

    test('rounds to one decimal place', () => {
      expect(formatFileSize(1536)).toBe('1.5 KB');
      expect(formatFileSize(2.5 * 1024 * 1024)).toBe('2.5 MB');
      expect(formatFileSize(3.75 * 1024 * 1024)).toBe('3.8 MB');
    });
  });

  describe('constants', () => {
    test('MAX_FILE_SIZE is 10 MB', () => {
      expect(MAX_FILE_SIZE).toBe(10 * 1024 * 1024);
    });

    test('ALLOWED_EXTENSIONS includes all required types', () => {
      const requiredExtensions = ['.pdf', '.xml', '.mxl', '.txt', '.png', '.jpg', '.jpeg', '.gif', '.mp3', '.wav'];
      requiredExtensions.forEach(ext => {
        expect(ALLOWED_EXTENSIONS).toContain(ext);
      });
    });
  });
});
