'use strict';

const Crypto = require('crypto');

const Boom = require('@hapi/boom');


const internals = {};


// Generate a cryptographically strong pseudo-random data

exports.randomString = function (size) {

    const buffer = exports.randomBits((size + 1) * 6);
    const string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
    return string.slice(0, size);
};


// Generate a cryptographically strong pseudo-random alphanum data

exports.randomAlphanumString = function (size) {

    let result = '';

    while (result.length < size) {
        const buffer = exports.randomBits((size + 1) * 6);
        result += buffer.toString('base64').replace(/[^a-zA-Z0-9]/g, '');
    }

    return result.slice(0, size);
};


// Return a random string of digits

exports.randomDigits = function (size) {

    const digits = [];

    let buffer = internals.random(size * 2);            // Provision twice the amount of bytes needed to increase chance of single pass
    let pos = 0;

    while (digits.length < size) {
        if (pos >= buffer.length) {
            buffer = internals.random(size * 2);
            pos = 0;
        }

        if (buffer[pos] < 250) {
            digits.push(buffer[pos] % 10);
        }

        ++pos;
    }

    return digits.join('');
};


// Generate a buffer of random bits

exports.randomBits = function (bits) {

    if (!bits ||
        bits < 0) {

        throw Boom.internal('Invalid random bits count');
    }

    const bytes = Math.ceil(bits / 8);
    return internals.random(bytes);
};


exports.fixedTimeComparison = function (a, b) {

    try {
        return Crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
    }
    catch (err) {
        return false;
    }
};


internals.random = function (bytes) {

    try {
        return Crypto.randomBytes(bytes);
    }
    catch (err) {
        throw Boom.internal('Failed generating random bits: ' + err.message);
    }
};
