// Password hashing using Node's built-in scrypt + a per-hash random salt.
//
// No external dependency (bcrypt would need a native build step). scrypt is
// memory-hard and well suited for interactive logins. Hash format:
//   "<saltHex>:<hashHex>"  (salt is 16 bytes, hash is 64 bytes)
//
// verify() is async (crypto.scrypt, non-blocking) so a login attempt cannot pin
// the event loop and stall every other request — a synchronous scrypt was a
// cheap CPU-DoS vector. DUMMY_HASH is a real hash for the "unknown user" path so
// login timing does not leak which usernames exist.

const crypto = require('crypto');

const KEYLEN = 64;
const DUMMY_SALT = '00000000000000000000000000000000';
const DUMMY_HASH = `${DUMMY_SALT}:${crypto.scryptSync('nextjobs-dummy', DUMMY_SALT, KEYLEN).toString('hex')}`;

function hash(password) {
  const salt = crypto.randomBytes(16).toString('hex');
  const out = crypto.scryptSync(password, salt, KEYLEN).toString('hex');
  return `${salt}:${out}`;
}

// Resolve to true/false. Always runs a full scrypt (constant work) before
// deciding, so the caller cannot tell a wrong password from a bad hash format.
function verify(password, stored) {
  return new Promise((resolve) => {
    if (typeof stored !== 'string' || !stored.includes(':')) return resolve(false);
    const idx = stored.indexOf(':');
    const salt = stored.slice(0, idx);
    const expected = stored.slice(idx + 1);
    if (!salt || !expected) return resolve(false);
    crypto.scrypt(String(password == null ? '' : password), salt, KEYLEN, (err, buf) => {
      if (err) return resolve(false);
      const computed = buf.toString('hex');
      if (computed.length !== expected.length) return resolve(false);
      // Constant-time compare to avoid timing side channels.
      resolve(crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(expected)));
    });
  });
}

module.exports = { hash, verify, DUMMY_HASH };