/**
 * Servicio: checksumService
 *
 * Calcula sumas y metadatos de un fichero de forma eficiente usando streams.
 * Las funciones principales procesan el archivo en streaming para producir:
 * - `md5` (hex)
 * - `sha1` (hex)
 * - `crc32` (hex, 8 caracteres)
 * - `size` (bytes)
 *
 * `computeHashes(filePath)` devuelve un objeto con los valores anteriores y
 * está pensado para usarse durante la importación/normalización de ROMs.
 */
import fs from 'fs';
import { createHash } from 'crypto';

function makeCRCTable(): Uint32Array {
  const table = new Uint32Array(256);
  for (let n = 0; n < 256; n++) {
    let c = n;
    for (let k = 0; k < 8; k++) {
      if (c & 1) c = 0xedb88320 ^ (c >>> 1);
      else c = c >>> 1;
    }
    table[n] = c >>> 0;
  }
  return table;
}

const CRC_TABLE = makeCRCTable();

function updateCrc(crc: number, buf: Buffer): number {
  let c = crc >>> 0;
  for (let i = 0; i < buf.length; i++) {
    c = (CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8)) >>> 0;
  }
  return c >>> 0;
}

export async function computeHashes(filePath: string): Promise<{
  size: number;
  md5: string;
  sha1: string;
  crc32: string;
}> {
  return new Promise((resolve, reject) => {
    const md5 = createHash('md5');
    const sha1 = createHash('sha1');

    let size = 0;
    let crc = 0xffffffff >>> 0;

    const rs = fs.createReadStream(filePath);

    rs.on('error', (err) => reject(err));

    rs.on('data', (chunk: Buffer) => {
      md5.update(chunk);
      sha1.update(chunk);
      size += chunk.length;
      crc = updateCrc(crc, chunk);
    });

    rs.on('end', () => {
      const md5sum = md5.digest('hex');
      const sha1sum = sha1.digest('hex');
      const final = (crc ^ 0xffffffff) >>> 0;
      const crcHex = final.toString(16).padStart(8, '0').toLowerCase();

      resolve({ size, md5: md5sum, sha1: sha1sum, crc32: crcHex });
    });
  });
}

export default computeHashes;
