'use strict'

const uuidv4 = require('./uuid-node')
const parser = require('uuid-parse')
const maxInt = Math.pow(2, 31) - 1
const Buffer = require('buffer').Buffer
const base64Padding = Buffer.from('==', 'base64')

function hyperid (opts) {
  let fixedLength = false
  let urlSafe = false
  if (typeof opts === 'boolean') {
    fixedLength = opts
  } else {
    opts = opts || {}
    urlSafe = !!opts.urlSafe
    fixedLength = !!opts.fixedLength
  }

  generate.uuid = uuidv4()
  generate.decode = decode

  let id = baseId(generate.uuid, urlSafe)
  let count = Math.floor(opts.startFrom || 0)

  if (isNaN(count) || !(maxInt > count && count >= 0)) {
    throw new Error([
      `when passed, opts.startFrom must be a number between 0 and ${maxInt}.`,
      'Only the integer part matters.',
      `- got: ${opts.startFrom}`
    ].join('\n'))
  }

  return generate

  function generate () {
    let result
    if (count === maxInt) {
      generate.uuid = uuidv4()
      id = baseId(generate.uuid, urlSafe) // rebase
      count = 0
    }
    if (fixedLength) {
      result = id + `0000000000${count}`.slice(-10)
    } else {
      result = id + count
    }
    count = (count + 1) | 0
    return result
  }
}

function baseId (id, urlSafe) {
  const base64Id = Buffer.concat([Buffer.from(parser.parse(id)), base64Padding]).toString('base64')
  if (urlSafe) {
    return base64Id.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '-')
  }
  return base64Id.replace(/=+$/, '/')
}

function decode (id, opts) {
  opts = opts || {}
  const urlSafe = !!opts.urlSafe

  if (urlSafe) {
    id = id.replace(/-([^-]*)$/, '/' + '$1')
      .replace(/-/g, '+')
      .replace(/_/g, '/')
  }

  if (id.length < 22) {
    return null
  }
  const lastSlashIndex = id.lastIndexOf('/')
  if (lastSlashIndex === -1) {
    return null
  }
  const uuidPart = id.substring(0, lastSlashIndex)
  const countPart = Number(id.substring(lastSlashIndex + 1))
  if (!uuidPart || isNaN(countPart)) {
    return null
  }

  const result = {
    uuid: parser.unparse(Buffer.from(uuidPart + '==', 'base64')),
    count: countPart
  }

  return result
}

module.exports = hyperid
module.exports.decode = decode
