// location: loaders/relay-loader.js
var path = require('path');
var fs = require('fs');
var loaderUtils = require('loader-utils');
var babel = require('babel-core');
var babelRelayPluginConstructor = require('babel-relay-plugin');

// Some of this code is shamelessly stolen from babel-loader

/**
 * Error thrown by Babel formatted to conform to Webpack reporting.
 */
function BabelLoaderError(name, message, codeFrame, hideStack, error) {
  Error.call(this);
  Error.captureStackTrace(this, BabelLoaderError);

  this.name = 'BabelLoaderError';
  this.message = formatMessage(name, message, codeFrame);
  this.hideStack = hideStack;
  this.error = error;
}

BabelLoaderError.prototype = Object.create(Error.prototype);
BabelLoaderError.prototype.constructor = BabelLoaderError;

var STRIP_FILENAME_RE = /^[^:]+: /;

var formatMessage = function(name, message, codeFrame) {
  return (name ? name + ': ' : '') + message + '\n\n' + codeFrame + '\n';
};

function transpile(source, options, callback) {
	var result;
	try {
		result = babel.transform(source, options);
	} catch (error) {
		if (error.message && error.codeFrame) {
			var message = error.message;
			var name;
			var hideStack;
			if (error instanceof SyntaxError) {
				message = message.replace(STRIP_FILENAME_RE, '');
				name = 'SyntaxError';
				hideStack = true;
			} else if (error instanceof TypeError) {
				message = message.replace(STRIP_FILENAME_RE, '');
				hideStack = true;
			}
			return callback(
				new BabelLoaderError(
					name,
					message,
					error.codeFrame,
					hideStack,
					error
				)
			);

		} else {
			callback(error);
		}
	}

	var code = result.code;
	var map = result.map;

	if (map && (!map.sourcesContent || !map.sourcesContent.length)) {
		map.sourcesContent = [source];
	}

	callback(null, code, map);
}

module.exports = function(source, inputSourceMap) {
	this.cacheable();
	var schemaFile = (this.options.relay && this.options.relay.schemaFile);
	var callback = this.async();
	if (!schemaFile) {
		return callback(new Error("Specify schema file in webpack config at relay.schemaFile. Must be absolute path"));
	}
	var webpackRemainingChain = loaderUtils.getRemainingRequest(this).split('!');
	var filename = webpackRemainingChain[webpackRemainingChain.length - 1];
	// I disable the ts-loaders behavior of depending on every single file this file actually depends on.
	// To be completely 100% accurate this is what is needed. However it also slows down the entire system
	// by quite a bit. (Isolated simple example cut a few seconds off hot-reload times, which I consider significant).
	// This means if you change exports in a file (like remove a default export) other files won't be recompiled because
	// of it. This might prove to be a bad idea, luckily it is easily removed.
	this.clearDependencies();
	this.addDependency(filename);
	if (this.options.relay && this.options.relay.omitFiles && this.options.relay.omitFiles.indexOf(filename) >= 0) {
		return callback(null, source, inputSourceMap);
	}

	this.addDependency(schemaFile);
	fs.readFile(schemaFile, 'utf-8', function(err, result) {
		if (err) {
			return callback(err);
		}

		var json;
		try {
			json = JSON.parse(result);
		} catch (e) {
			return callback(err);
		}

		var plugin = babelRelayPluginConstructor(json.data);
		var babelOpts = {
			plugins: [plugin, require('babel-plugin-transform-es2015-template-literals')],
			filename: filename,
			inputSourceMap: inputSourceMap,
			sourceRoot: process.cwd(),
			env: process.env.BABEL_ENV || process.env.NODE_ENV,
			sourceMap: true,
		};

		transpile(source, babelOpts, callback);
	});
}