'use strict';
// Based on https://github.com/babel/babel-loader/blob/15df92fafd58ec53ba88efa22de7b2cee5e65fcc/src/cache.js
const fs = require('fs');
const os = require('os');
const path = require('path');
const crypto = require('crypto');
const makeDir = require('make-dir');
const findCacheDir = require('find-cache-dir');
const transform = require('./transform');

let directory;

/**
 * Build the filename for the cached file
 *
 * @param  {String} source  Original contents of the file to be cached
 * @param  {Object} options Options passed to importJsx
 * @param  {String} version Version of import-jsx
 *
 * @return {String}
 */
const filename = (source, options, version) => {
	const hash = crypto.createHash('md5');
	const contents = JSON.stringify({source, options, version});
	hash.update(contents);

	return hash.digest('hex') + '.js';
};

/**
 * Handle the cache
 *
 * @params {String} directory
 * @params {Object} parameters
 */
const handleCache = (directory, parameters) => {
	const {modulePath, options, source, version} = parameters;

	if (!options.cache) {
		return transform(source, options, modulePath);
	}

	const file = path.join(directory, filename(source, options, version));

	try {
		// No errors mean that the file was previously cached
		// we just need to return it
		return fs.readFileSync(file).toString();
		// eslint-disable-next-line no-unused-vars
	} catch (error) {}

	const fallback = directory !== os.tmpdir();

	// Make sure the directory exists.
	try {
		makeDir.sync(directory);
	} catch (error) {
		if (fallback) {
			return handleCache(os.tmpdir(), parameters);
		}

		throw error;
	}

	// Otherwise just transform the file
	// return it to the user asap and write it in cache
	const result = transform(source, options, modulePath);

	try {
		fs.writeFileSync(file, result);
	} catch (error) {
		if (fallback) {
			// Fallback to tmpdir if node_modules folder not writable
			return handleCache(os.tmpdir(), parameters);
		}

		throw error;
	}

	return result;
};

/**
 * Retrieve file from cache, or create a new one for future reads
 *
 * @param  {Object}   parameters
 * @param  {String}   parameters.modulePath
 * @param  {String}   parameters.source     Original contents of the file to be cached
 * @param  {Object}   parameters.options    Options passed to importJsx
 * @param  {String}   parameters.version    Version of import-jsx
 */
module.exports = parameters => {
	if (!directory) {
		directory = findCacheDir({name: 'import-jsx'}) || os.tmpdir();
	}

	return handleCache(directory, parameters);
};
