import { describe, expect, test } from 'vitest';
import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from '../subgraph.js';

describe('uint256ToBytesLittleEndian', () => {
  test('converts zero to 32 zero bytes', () => {
    expect(uint256ToBytesLittleEndian(0n)).toEqual(new Uint8Array(32));
  });

  test('converts 1 to [1, ...31 zeros]', () => {
    const expected = new Uint8Array(32);
    expected[0] = 1;
    expect(uint256ToBytesLittleEndian(1n)).toEqual(expected);
  });

  test('converts 3 to [3, ...31 zeros]', () => {
    const result = uint256ToBytesLittleEndian(3n);
    let hexString = '0x';
    for (let i = 0; i < result.length; i++) {
      hexString += result[i].toString(16).padStart(2, '0');
    }
    expect(hexString).toEqual('0x03' + '00'.repeat(31));
  });

  test('converts 256 to [0, 1, ...30 zeros] (second byte)', () => {
    const expected = new Uint8Array(32);
    expected[1] = 1;
    expect(uint256ToBytesLittleEndian(256n)).toEqual(expected);
  });

  test('converts 65536 to [0, 0, 1, ...29 zeros] (third byte)', () => {
    const expected = new Uint8Array(32);
    expected[2] = 1;
    expect(uint256ToBytesLittleEndian(65536n)).toEqual(expected);
  });

  test('converts 16777216 (2^24) to [0, 0, 0, 1, ...28 zeros] (fourth byte)', () => {
    const expected = new Uint8Array(32);
    expected[3] = 1;
    expect(uint256ToBytesLittleEndian(16777216n)).toEqual(expected);
  });

  test('converts 2^32 to [0, 0, 0, 0, 1, ...27 zeros] (fifth byte)', () => {
    const expected = new Uint8Array(32);
    expected[4] = 1;
    expect(uint256ToBytesLittleEndian(2n ** 32n)).toEqual(expected);
  });

  test('converts max uint256 to all 0xff bytes', () => {
    expect(uint256ToBytesLittleEndian(2n ** 256n - 1n)).toEqual(new Uint8Array(32).fill(255));
  });

  test('returns a 32-byte Uint8Array', () => {
    const result = uint256ToBytesLittleEndian(12345n);
    expect(result).toBeInstanceOf(Uint8Array);
    expect(result.length).toBe(32);
  });
});

describe('bytesToUint256LittleEndian', () => {
  test('converts four zero bytes to zero', () => {
    expect(bytesToUint256LittleEndian(new Uint8Array([0, 0, 0, 0]))).toBe(0n);
  });

  test('converts [1, 0, 0, 0] to 1', () => {
    expect(bytesToUint256LittleEndian(new Uint8Array([1, 0, 0, 0]))).toBe(1n);
  });

  test('converts [0, 1, 0, 0] to 256', () => {
    expect(bytesToUint256LittleEndian(new Uint8Array([0, 1, 0, 0]))).toBe(256n);
  });

  test('converts [255, 255, 255, 255] to 0xffffffff', () => {
    expect(bytesToUint256LittleEndian(new Uint8Array([255, 255, 255, 255]))).toBe(0xffffffffn);
  });

  test('returns a bigint', () => {
    const result = bytesToUint256LittleEndian(new Uint8Array([3, 0, 0, 0]));
    expect(typeof result).toBe('bigint');
  });
});

describe('roundtrip: uint256ToBytesLittleEndian -> bytesToUint256LittleEndian', () => {
  test('roundtrip preserves value for common position IDs', () => {
    const values = [0n, 1n, 2n, 3n, 100n, 255n, 256n, 12345n, 65535n, 16777215n];
    for (const val of values) {
      const bytes = uint256ToBytesLittleEndian(val);
      expect(bytesToUint256LittleEndian(bytes)).toBe(val);
    }
  });

  test('roundtrip preserves values >= 2^32', () => {
    const values = [2n ** 32n, 2n ** 32n + 1n, 2n ** 128n, 2n ** 256n - 1n];
    for (const val of values) {
      const bytes = uint256ToBytesLittleEndian(val);
      expect(bytesToUint256LittleEndian(bytes)).toBe(val);
    }
  });
});
