// Auth utilities — password hashing (scrypt), tokens, and TOTP MFA.
// Uses only Node's built-in crypto, no external auth deps.
const crypto = require('crypto');

// ---- Passwords (scrypt) ----
function hashPassword(password, salt = crypto.randomBytes(16).toString('hex')) {
  const hash = crypto.scryptSync(password, salt, 64).toString('hex');
  return { hash, salt };
}
function verifyPassword(password, salt, expectedHash) {
  const hash = crypto.scryptSync(password, salt, 64).toString('hex');
  return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(expectedHash));
}

// ---- Random tokens ----
const token = (bytes = 24) => crypto.randomBytes(bytes).toString('hex');
const id = () => crypto.randomBytes(8).toString('hex');
// Deterministic hash for storing high-value tokens (e.g. refresh tokens) at rest.
const hashToken = (t) => crypto.createHash('sha256').update(String(t)).digest('hex');
const numericCode = (digits = 6) =>
  String(crypto.randomInt(0, 10 ** digits)).padStart(digits, '0');

// ---- TOTP (RFC 6238), SHA-1, 30s, 6 digits ----
const B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
function base32Encode(buf) {
  let bits = 0, value = 0, out = '';
  for (const byte of buf) {
    value = (value << 8) | byte; bits += 8;
    while (bits >= 5) { out += B32[(value >>> (bits - 5)) & 31]; bits -= 5; }
  }
  if (bits > 0) out += B32[(value << (5 - bits)) & 31];
  return out;
}
function base32Decode(str) {
  const clean = str.replace(/=+$/, '').toUpperCase();
  let bits = 0, value = 0; const out = [];
  for (const ch of clean) {
    const idx = B32.indexOf(ch);
    if (idx === -1) continue;
    value = (value << 5) | idx; bits += 5;
    if (bits >= 8) { out.push((value >>> (bits - 8)) & 0xff); bits -= 8; }
  }
  return Buffer.from(out);
}
function newMfaSecret() {
  return base32Encode(crypto.randomBytes(20));
}
function totp(secret, timeStep = Math.floor(Date.now() / 30000)) {
  const key = base32Decode(secret);
  const buf = Buffer.alloc(8);
  buf.writeBigUInt64BE(BigInt(timeStep));
  const hmac = crypto.createHmac('sha1', key).update(buf).digest();
  const offset = hmac[hmac.length - 1] & 0xf;
  const code =
    ((hmac[offset] & 0x7f) << 24) |
    ((hmac[offset + 1] & 0xff) << 16) |
    ((hmac[offset + 2] & 0xff) << 8) |
    (hmac[offset + 3] & 0xff);
  return String(code % 1_000_000).padStart(6, '0');
}
function verifyTotp(secret, code, window = 1) {
  if (!/^\d{6}$/.test(String(code || ''))) return false;
  const step = Math.floor(Date.now() / 30000);
  for (let i = -window; i <= window; i++) {
    if (totp(secret, step + i) === String(code)) return true;
  }
  return false;
}
function otpauthUrl(secret, email, issuer = 'RemoteAccess') {
  return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(email)}` +
         `?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=SHA1&digits=6&period=30`;
}

module.exports = {
  hashPassword, verifyPassword, token, id, hashToken, numericCode,
  newMfaSecret, totp, verifyTotp, otpauthUrl,
};
