// ============================================================
// Unified password hashing — argon2id preferred, bcrypt fallback
// Transparent migration: on successful login with a bcrypt hash,
// the row is rehashed as argon2id and updated in place.
// ============================================================

var bcrypt = require('bcryptjs');
var argon2 = null;
try {
  argon2 = require('argon2');
} catch (e) {
  // argon2 optional — install with `npm install argon2` to enable.
  // Until then, registration + change-password fall back to bcrypt(12).
  console.warn('[passwords] argon2 not installed — using bcrypt only. Install argon2 to enable migration.');
}

var BCRYPT_ROUNDS = 12;
var ARGON2_OPTS = {
  type: 2, // argon2id
  memoryCost: 19456,   // 19 MiB — OWASP 2023 recommended
  timeCost: 2,
  parallelism: 1
};

function isArgon2Hash(h) { return typeof h === 'string' && h.indexOf('$argon2') === 0; }
function isBcryptHash(h) { return typeof h === 'string' && /^\$2[aby]\$/.test(h); }

async function hash(password) {
  if (argon2) return argon2.hash(password, ARGON2_OPTS);
  return bcrypt.hash(password, BCRYPT_ROUNDS);
}

async function verify(password, storedHash) {
  if (!storedHash) return false;
  if (isArgon2Hash(storedHash)) {
    if (!argon2) throw new Error('argon2 hash encountered but argon2 package not installed');
    return argon2.verify(storedHash, password);
  }
  if (isBcryptHash(storedHash)) {
    return bcrypt.compare(password, storedHash);
  }
  return false;
}

// Returns a new argon2 hash if migration is desired, else null.
async function maybeRehash(password, storedHash) {
  if (!argon2) return null;
  if (isArgon2Hash(storedHash)) return null; // already argon2
  return argon2.hash(password, ARGON2_OPTS);
}

module.exports = { hash: hash, verify: verify, maybeRehash: maybeRehash, hasArgon2: function(){ return !!argon2; } };
