/* eslint-env es6 */
const cleancss = require('clean-css');
const path = require('path');

const UMD_WRAPPER_RE = /(\(function \(global, factory\) \{)((?:\s.*?)*)(\}\(this,)/;
const CJS_FACTORY_RE = /(module.exports = )(factory\(.*?\))( :)/;
const AMD_FACTORY_RE = /(define\()(.*?, factory)(\) :)/;

function optional(config = {}) {
	return {
		name: 'optional',
		renderChunk(code, chunk, options) {
			if (options.format !== 'umd') {
				this.error('only UMD format is currently supported');
			}

			const wrapper = UMD_WRAPPER_RE.exec(code);
			const include = config.include;
			if (!wrapper) {
				this.error('failed to parse the UMD wrapper');
			}

			let content = wrapper[2];
			let factory = (CJS_FACTORY_RE.exec(content) || [])[2];
			let updated = false;

			for (let lib of chunk.imports) {
				if (!include || include.indexOf(lib) !== -1) {
					const regex = new RegExp(`require\\('${lib}'\\)`);
					if (!regex.test(factory)) {
						this.error(`failed to parse the CJS require for ${lib}`);
					}

					// We need to write inline try / catch with explicit require
					// in order to enable statical extraction of dependencies:
					// try { return require('moment'); } catch(e) {}
					const loader = `function() { try { return require('${lib}'); } catch(e) { } }()`;
					factory = factory.replace(regex, loader);
					updated = true;
				}
			}

			if (!updated) {
				return;
			}

			// Replace the CJS factory by our updated one.
			content = content.replace(CJS_FACTORY_RE, `$1${factory}$3`);

			// Replace the AMD factory by our updated one: we need to use the
			// following AMD form in order to be able to try/catch require:
			// define(['require'], function(require) { ... require(...); ... })
			// https://github.com/amdjs/amdjs-api/wiki/AMD#using-require-and-exports
			content = content.replace(AMD_FACTORY_RE, `$1['require'], function(require) { return ${factory}; }$3`);

			return code.replace(UMD_WRAPPER_RE, `$1${content}$3`);
		}
	};
}

// https://github.com/chartjs/Chart.js/issues/5208
function stylesheet(config = {}) {
	const minifier = new cleancss();
	const styles = [];

	return {
		name: 'stylesheet',
		transform(code, id) {
			// Note that 'id' can be mapped to a CJS proxy import, in which case
			// 'id' will start with 'commonjs-proxy', so let's first check if we
			// are importing an existing css file (i.e. startsWith()).
			if (!id.startsWith(path.resolve('.')) || !id.endsWith('.css')) {
				return;
			}

			if (config.minify) {
				code = minifier.minify(code).styles;
			}

			// keep track of all imported stylesheets (already minified)
			styles.push(code);

			return {
				code: 'export default ' + JSON.stringify(code)
			};
		},
		generateBundle(opts, bundle) {
			if (!config.extract) {
				return;
			}

			const entry = Object.keys(bundle).find(v => bundle[v].isEntry);
			const name = (entry || '').replace(/\.js$/i, '.css');
			if (!name) {
				this.error('failed to guess the output file name');
			}

			this.emitFile({
				type: 'asset',
				source: styles.filter(v => !!v).join(''),
				fileName: name
			});
		}
	};
}

module.exports = {
	optional,
	stylesheet
};
