/* eslint no-sync:0 */
'use strict'

// Import
const fsUtil = require('graceful-fs')
const pathUtil = require('path')

// =====================================
// Define Module

const safefs = {
	// =====================================
	// Our own custom functions

	// Get the parent path
	getParentPathSync(path) {
		return (
			path
				// remove trailing slashes
				.replace(/[/\\]$/, '')
				// remove last directory
				.replace(/[/\\][^/\\]+$/, '')
		)
	},

	// Ensure path exists
	// next(err, existed)
	ensurePath(path, opts, next) {
		// Prepare
		if (next == null) {
			next = opts
			opts = null
		}
		opts = opts || {}

		// Check
		safefs.exists(path, function(exists) {
			// Error
			if (exists) return next(null, true)

			// Success
			const parentPath = safefs.getParentPathSync(path)
			safefs.ensurePath(parentPath, opts, function(err) {
				// Error
				if (err) return next(err, false)

				// Success
				safefs.mkdir(path, opts.mode, function() {
					// ignore mkdir error, as if it already exists, then we are winning

					safefs.exists(path, function(exists) {
						// Error
						if (!exists) {
							const err = new Error(`Failed to create the directory: ${path}`)
							return next(err, false)
						}

						// Success
						next(null, false)
					})
				})
			})
		})

		// Chain
		return safefs
	},

	// =====================================
	// Safe Wrappers for Standard Methods

	// Write File
	// next(err)
	writeFile(path, data, opts, next) {
		// Prepare
		if (next == null) {
			next = opts
			opts = null
		}

		// Ensure path
		safefs.ensurePath(pathUtil.dirname(path), opts, function(err) {
			// Error
			if (err) return next(err)

			// Write data
			fsUtil.writeFile(path, data, opts, next)
		})

		// Chain
		return safefs
	},

	// Append File
	// next(err)
	appendFile(path, data, opts, next) {
		// Prepare
		if (next == null) {
			next = opts
			opts = null
		}

		// Ensure path
		safefs.ensurePath(pathUtil.dirname(path), opts, function(err) {
			// Error
			if (err) return next(err)

			// Write data
			fsUtil.appendFile(path, data, opts, next)
		})

		// Chain
		return safefs
	},

	// Mkdir
	// next(err)
	mkdir(path, mode, next) {
		// Prepare
		if (next == null) {
			next = mode
			mode = null
		}
		if (mode == null) {
			/* eslint no-bitwise:0, no-magic-numbers:0 */
			mode = 0o777 & ~process.umask()
		}

		// Action
		fsUtil.mkdir(path, mode, next)

		// Chain
		return safefs
	},

	// Unlink
	// don't error if the path doesn't already exist
	// next(err)
	unlink(path, next) {
		// Stat
		safefs.exists(path, function(exists) {
			if (exists === false) return next()
			fsUtil.unlink(path, next)
		})

		// Chain
		return safefs
	}
}

// Add any missing methods
Object.keys(fsUtil).forEach(function(key) {
	const value = fsUtil[key]
	// we do the `!safefs[key]` as we don't want to over-write our own enhancements
	// we do the `value.bind` check, as we may interate across trivial types
	// we do the `Function.prototype.bind` check, as underscore is a function that has it's own bind
	if (!safefs[key] && value && value.bind === Function.prototype.bind) {
		safefs[key] = value.bind(fsUtil)
	}
})

// Export
module.exports = safefs
