var crypto = require('crypto')

var url = require('../url')

var random
if (crypto.randomFill) {
  random = function (bytes, callback) {
    return crypto.randomFill(Buffer.allocUnsafe(bytes), callback)
  }
} else {
  random = crypto.randomBytes
}

/**
 * Generate secure URL-friendly unique ID. Non-blocking version.
 *
 * By default, ID will have 21 symbols to have a collision probability similar
 * to UUID v4.
 *
 * @param {number} [size=21] The number of symbols in ID.
 * @param {function} [callback] for environments without `Promise`.
 *
 * @return {Promise} Promise with random string.
 *
 * @example
 * const nanoidAsync = require('nanoid/async')
 * nanoidAsync.then(id => {
 *   model.id = id
 * })
 *
 * @name async
 * @function
 */
module.exports = function (size, callback, attempt) {
  size = size || 21

  if (!callback) {
    if (typeof Promise !== 'function') {
      throw new TypeError('Function callback is required')
    }
    return new Promise(function (resolve, reject) {
      module.exports(size, function (error, id) {
        if (error) {
          reject(error)
        } else {
          resolve(id)
        }
      })
    })
  } else if (typeof callback !== 'function') {
    throw new TypeError('Callback is not a function')
  }

  random(size, function (err, bytes) {
    if (err) {
      if (typeof attempt === 'undefined') attempt = 3
      attempt -= 1
      if (attempt === 0) {
        callback(err)
      } else {
        setTimeout(module.exports.bind(null, size, callback, attempt), 10)
      }
    } else {
      var id = ''
      while (0 < size--) {
        id += url[bytes[size] & 63]
      }
      callback(null, id)
    }
  })
}