/**
 * Way data is stored for this database
 *
 * This version is the browser version and uses [localforage]{@link https://github.com/localForage/localForage} which chooses the best option depending on user browser (IndexedDB then WebSQL then localStorage).
 * @module storageBrowser
 * @see module:storage
 * @see module:storageReactNative
 * @private
 */

const localforage = require('localforage')

// Configure localforage to display NeDB name for now. Would be a good idea to let user use his own app name
const store = localforage.createInstance({
  name: 'NeDB',
  storeName: 'nedbdata'
})

/**
 * Returns Promise<true> if file exists.
 *
 * @param {string} file
 * @return {Promise<boolean>}
 * @async
 * @alias module:storageBrowser.existsAsync
 */
const existsAsync = async file => {
  try {
    const value = await store.getItem(file)
    if (value !== null) return true // Even if value is undefined, localforage returns null
    return false
  } catch (error) {
    return false
  }
}

/**
 * Moves the item from one path to another.
 * @param {string} oldPath
 * @param {string} newPath
 * @return {Promise<void>}
 * @alias module:storageBrowser.renameAsync
 * @async
 */
const renameAsync = async (oldPath, newPath) => {
  try {
    const value = await store.getItem(oldPath)
    if (value === null) await store.removeItem(newPath)
    else {
      await store.setItem(newPath, value)
      await store.removeItem(oldPath)
    }
  } catch (err) {
    console.warn('An error happened while renaming, skip')
  }
}

/**
 * Saves the item at given path.
 * @param {string} file
 * @param {string} data
 * @param {object} [options]
 * @return {Promise<void>}
 * @alias module:storageBrowser.writeFileAsync
 * @async
 */
const writeFileAsync = async (file, data, options) => {
  // Options do not matter in browser setup
  try {
    await store.setItem(file, data)
  } catch (error) {
    console.warn('An error happened while writing, skip')
  }
}

/**
 * Append to the item at given path.
 * @function
 * @param {string} filename
 * @param {string} toAppend
 * @param {object} [options]
 * @return {Promise<void>}
 * @alias module:storageBrowser.appendFileAsync
 * @async
 */
const appendFileAsync = async (filename, toAppend, options) => {
  // Options do not matter in browser setup
  try {
    const contents = (await store.getItem(filename)) || ''
    await store.setItem(filename, contents + toAppend)
  } catch (error) {
    console.warn('An error happened appending to file writing, skip')
  }
}

/**
 * Read data at given path.
 * @function
 * @param {string} filename
 * @param {object} [options]
 * @return {Promise<Buffer>}
 * @alias module:storageBrowser.readFileAsync
 * @async
 */
const readFileAsync = async (filename, options) => {
  try {
    return (await store.getItem(filename)) || ''
  } catch (error) {
    console.warn('An error happened while reading, skip')
    return ''
  }
}

/**
 * Async version of {@link module:storageBrowser.unlink}.
 * @function
 * @param {string} filename
 * @return {Promise<void>}
 * @async
 * @alias module:storageBrowser.unlink
 */
const unlinkAsync = async filename => {
  try {
    await store.removeItem(filename)
  } catch (error) {
    console.warn('An error happened while unlinking, skip')
  }
}

/**
 * Shim for {@link module:storage.mkdirAsync}, nothing to do, no directories will be used on the browser.
 * @function
 * @param {string} path
 * @param {object} [options]
 * @return {Promise<void|string>}
 * @alias module:storageBrowser.mkdirAsync
 * @async
 */
const mkdirAsync = (path, options) => Promise.resolve()

/**
 * Shim for {@link module:storage.ensureParentDirectoryExistsAsync}, nothing to do, no directories will be used on the browser.
 * @function
 * @param {string} file
 * @param {number} [mode]
 * @return {Promise<void|string>}
 * @alias module:storageBrowser.ensureParentDirectoryExistsAsync
 * @async
 */
const ensureParentDirectoryExistsAsync = async (file, mode) => Promise.resolve()

/**
 * Shim for {@link module:storage.ensureDatafileIntegrityAsync}, nothing to do, no data corruption possible in the browser.
 * @param {string} filename
 * @return {Promise<void>}
 * @alias module:storageBrowser.ensureDatafileIntegrityAsync
 */
const ensureDatafileIntegrityAsync = (filename) => Promise.resolve()

/**
 * Fully write or rewrite the datafile, immune to crashes during the write operation (data will not be lost)
 * * @param {string} filename
 * @param {string[]} lines
 * @return {Promise<void>}
 * @alias module:storageBrowser.crashSafeWriteFileLinesAsync
 */
const crashSafeWriteFileLinesAsync = async (filename, lines) => {
  lines.push('') // Add final new line
  await writeFileAsync(filename, lines.join('\n'))
}

// Interface
module.exports.existsAsync = existsAsync

module.exports.renameAsync = renameAsync

module.exports.writeFileAsync = writeFileAsync

module.exports.crashSafeWriteFileLinesAsync = crashSafeWriteFileLinesAsync

module.exports.appendFileAsync = appendFileAsync

module.exports.readFileAsync = readFileAsync

module.exports.unlinkAsync = unlinkAsync

module.exports.mkdirAsync = mkdirAsync

module.exports.ensureDatafileIntegrityAsync = ensureDatafileIntegrityAsync

module.exports.ensureParentDirectoryExistsAsync = ensureParentDirectoryExistsAsync
