// src/utils/checksum.test.ts
/**
 * @vitest-environment jsdom
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { generateFileChecksum } from './checksum';

describe('generateFileChecksum', () => {
  it('should generate the correct SHA-256 checksum for a simple text file', async () => {
    // Arrange
    const fileContent = 'Hello, world!';
    const file = new File([fileContent], 'hello.txt', { type: 'text/plain' });
    // The known SHA-256 hash for "Hello, world!"
    const expectedChecksum = '315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3';

    // Act
    const checksum = await generateFileChecksum(file);

    // Assert
    expect(checksum).toBe(expectedChecksum);
  });

  it('should generate the correct SHA-256 checksum for an empty file', async () => {
    // Arrange
    const file = new File([], 'empty.txt', { type: 'text/plain' });
    // The known SHA-256 hash for an empty input
    const expectedChecksum = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';

    // Act
    const checksum = await generateFileChecksum(file);

    // Assert
    expect(checksum).toBe(expectedChecksum);
  });

  it('should generate a consistent checksum for a file with binary data', async () => {
    // Arrange: Create a file with some arbitrary binary data
    const binaryData = new Uint8Array([0, 1, 2, 3, 255, 254, 128]);
    const file = new File([binaryData], 'data.bin', { type: 'application/octet-stream' });
    // SHA-256 hash for [0, 1, 2, 3, 255, 254, 128]
    const expectedChecksum = '654b06382635b60c931f187f4c16fc222a6a276dec429a56bfb5fe3b313de1c4';

    // Act
    const checksum = await generateFileChecksum(file);

    // Assert
    expect(checksum).toBe(expectedChecksum);
  });

  describe('with fallback mechanisms', () => {

    beforeEach(() => {
      // Mock console.warn to prevent logs from appearing in test output
      vi.spyOn(console, 'warn').mockImplementation(() => {});
    });

    afterEach(() => {
      // Restore any mocked globals
      vi.restoreAllMocks();
    });

    it('should use FileReader fallback if file.arrayBuffer is not a function', async () => {
      const fileContent = 'fallback test';
      // FIX: Use TextEncoder to create a Uint8Array. Passing a Blob to the File constructor
      // in some JSDOM versions can result in the string "[object Blob]" being read instead of content.
      // Uint8Array is handled reliably by the File constructor and FileReader.
      const file = new File([new TextEncoder().encode(fileContent)], 'test.txt', { type: 'text/plain' });
      // Simulate an environment where file.arrayBuffer does not exist to force the fallback.
      Object.defineProperty(file, 'arrayBuffer', { value: undefined });

      const checksum = await generateFileChecksum(file);
      // SHA-256 for "fallback test"
      expect(checksum).toBe('33d303e4c33c5a1e695575069916f7533383a9552887393d796c3a688b438a45');
      expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('file.arrayBuffer is not a function'));
    });

    it('should use FileReader fallback if file.arrayBuffer throws an error', async () => {
      const fileContent = 'error fallback';
      // Use TextEncoder to create a Uint8Array for reliable file content creation in JSDOM.
      const file = new File([new TextEncoder().encode(fileContent)], 'test.txt', { type: 'text/plain' });
      // Mock the function to throw an error
      vi.spyOn(file, 'arrayBuffer').mockRejectedValue(new Error('Simulated error'));

      const checksum = await generateFileChecksum(file);
      // SHA-256 for "error fallback"
      expect(checksum).toBe('a876a745353597a893f0a997b834155375399547535083925239618a1233525b');
      expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('file.arrayBuffer() threw an error'));
    });

    it('should throw an error if crypto.subtle is not available', async () => {
      const file = new File(['test'], 'test.txt', { type: 'text/plain' });

      // FIX: Use vi.stubGlobal to safely modify the read-only global.crypto object for this test.
      // It will be automatically restored by `vi.restoreAllMocks()` in afterEach.
      vi.stubGlobal('crypto', { ...global.crypto, subtle: undefined });

      await expect(generateFileChecksum(file)).rejects.toThrow();
    });
  });
});