const path = require('path');
const fs = require('fs');
const rimraf = require('rimraf');
const child_process = require('child_process');
const assert = require('assert');
const glob = require('glob');

if (process.env.COVERAGE) {
	require('reify');
	global.DEBUG = true;
}
const butternut = require(process.env.COVERAGE ? '../src/index.js' : process.env.TEST_MIN ? '../dist/butternut.min.js' : '../dist/butternut.cjs.js');

require('source-map-support').install();
require('console-group').install();

function equal(a, b) {
	assert.equal(showInvisibles(a), showInvisibles(b));
}

function showInvisibles(str) {
	return str
		.replace(/^ +/gm, spaces => repeat('•', spaces.length))
		.replace(/ +$/gm, spaces => repeat('•', spaces.length))
		.replace(/^\t+/gm, tabs => repeat('›   ', tabs.length))
		.replace(/\t+$/gm, tabs => repeat('›   ', tabs.length));
}

function repeat(str, times) {
	let result = '';
	while (times--)
		result += str;
	return result;
}

describe('butternut', function () {
	this.timeout( 10000 );

	fs.readdirSync('test/samples').forEach(file => {
		const samples = require('./samples/' + file);

		describe(path.basename(file), () => {
			samples.forEach(sample => {
				(sample.solo
					? it.only
					: sample.skip ? it.skip : it)(sample.description, () => {
					if (sample.error) {
						assert.throws(() => {
							butternut.squash(sample.input, sample.options);
						}, err => {
							return (
								err.message === sample.error.message &&
								err.pos === sample.error.pos &&
								err.loc.line === sample.error.loc.line &&
								err.loc.column === sample.error.loc.column
							);
						});
					} else {
						const { code } = butternut.squash(
							sample.input,
							sample.options
						);
						equal(code, sample.output);

						// idempotency test
						if (sample.idempotent !== false ) {
							equal(butternut.squash(code, sample.options).code, code);
						}
					}
				});
			});
		});
	});

	describe('fixtures', () => {

		try {
			fs.mkdirSync( `test/fixture/output` );
			fs.mkdirSync( `test/fixture/output/butternut` );
		} catch ( err ) {
			if ( err.code !== 'EEXIST' ) throw err;
		}

		fs.readdirSync('test/fixture/input').forEach(file => {
			const solo = ( file === '_test.js' && !!fs.readFileSync(path.join('test/fixture/input', file), 'utf-8'));

			(solo ? it.only : it)(path.basename(file), () => {
				const source = fs.readFileSync(path.join('test/fixture/input', file), 'utf-8');

				let result;

				try {
					result = butternut.squash(source, {
						check: true
					});

					fs.writeFileSync(`test/fixture/output/butternut/${file}`, `${result.code}\n//# sourceMappingURL=${result.map.toUrl()}`);
				} catch ( err ) {
					if ( err.repro ) {
						console.error( `Reproduction:\n-----------\n${err.repro.input}\n-----------\n${err.repro.output}\n-----------` );
					}

					throw err;
				}

				// try running the code
				let canRunInNode;
				try {
					require(`./fixture/input/${file}`);
					canRunInNode = true;
				} catch (error) {
					canRunInNode = false;
				}

				if ( canRunInNode ) {
					process.env.NODE_ENV = 'production'; // squelch warnings (TODO this doesn't work?)
					require(`./fixture/output/butternut/${file}`);
				}
			});
		});
	});

	describe('cli', () => {
		fs.readdirSync('test/cli').forEach(dir => {
			if (dir[0] === '.') return; // .DS_Store

			it(dir, done => {
				dir = path.resolve('test/cli', dir);
				rimraf.sync(path.resolve(dir, 'actual'));
				fs.mkdirSync(path.resolve(dir, 'actual'));

				var binFile = path.resolve(__dirname, '../bin/squash');
				var commandFile = path.resolve(dir, 'command.sh');

				var command = fs
					.readFileSync(commandFile, 'utf-8')
					.replace('squash', 'node ' + binFile);
				child_process.exec(
					command,
					{
						cwd: dir
					},
					(err, stdout, stderr) => {
						if (err) return done(err);

						if (stdout) console.log(stdout);
						if (stderr) console.error(stderr);

						function catalogue(subdir) {
							subdir = path.resolve(dir, subdir);

							return glob
								.sync('**/*.js?(.map)', { cwd: subdir })
								.sort()
								.map(name => {
									var contents = fs
										.readFileSync(path.resolve(subdir, name), 'utf-8')
										.trim();

									if (path.extname(name) === '.map') {
										contents = JSON.parse(contents);
									}

									return {
										name: name,
										contents: contents
									};
								});
						}

						var expected = catalogue('expected');
						var actual = catalogue('actual');

						try {
							assert.deepEqual(actual, expected);
							done();
						} catch (err) {
							done(err);
						}
					}
				);
			});
		});
	});
});

@@ -515,16 +515,25 @@
 ual(a, b
+, message
 ) %7B%0A%09ass
@@ -578,16 +578,25 @@
 ibles(b)
+, message
 );%0A%7D%0A%0Afu
@@ -1922,16 +1922,44 @@
 de, code
+, 'failed idempotency check'
 );%0A%09%09%09%09%09

make it easier to see when test failure is due to idempotency check