import { pbkdf2Sync, randomBytes, timingSafeEqual } from 'crypto';

const passwordHashScheme = 'pbkdf2-sha512';
const passwordHashIterations = 120000;
const passwordHashKeyLength = 64;
const passwordHashDigest = 'sha512';

const encodeBase64Url = (value: Buffer) => value.toString('base64url');

export const hashPassword = (rawPassword: string) => {
  const salt = randomBytes(16);
  const hash = pbkdf2Sync(rawPassword, salt, passwordHashIterations, passwordHashKeyLength, passwordHashDigest);

  return [
    passwordHashScheme,
    String(passwordHashIterations),
    encodeBase64Url(salt),
    encodeBase64Url(hash),
  ].join('$');
};

export const verifyPassword = (rawPassword: string, storedHash: string) => {
  const [scheme, iterationsText, saltText, expectedHashText] = storedHash.split('$');

  if (!scheme || !iterationsText || !saltText || !expectedHashText) {
    return false;
  }

  if (scheme !== passwordHashScheme) {
    return false;
  }

  const iterations = Number(iterationsText);

  if (!Number.isInteger(iterations) || iterations <= 0) {
    return false;
  }

  const salt = Buffer.from(saltText, 'base64url');
  const expectedHash = Buffer.from(expectedHashText, 'base64url');
  const candidateHash = pbkdf2Sync(rawPassword, salt, iterations, expectedHash.length, passwordHashDigest);

  if (candidateHash.length !== expectedHash.length) {
    return false;
  }

  return timingSafeEqual(candidateHash, expectedHash);
};
