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


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

const safefs = {

	// =====================================
	// Our own custom functions

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


	// Ensure path exists
	// next(err, existed)
	ensurePath: function (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: function (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: function (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: function (path, mode, next) {
		// Prepare
		if ( next == null ) {
			next = mode
			mode = null
		}
		if ( mode == null ) {
			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: function (path, next) {
		// Stat
		safefs.exists(path, function (exists) {
			if ( exists === false )  return next()
			fsUtil.unlink(path, next)
		})

		// Chain
		return safefs
	},

	// Exists
	// next(err)
	exists: function (path, next) {
		// Action
		(fsUtil.exists || pathUtil.exists)(path, next)

		// Chain
		return safefs
	},

	// Exits Sync
	// next(err)
	existsSync: function (path) {
		/* eslint no-sync: 0 */
		return (fsUtil.existsSync || pathUtil.existsSync)(path)
	}
}

// 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
export default safefs
