// @ts-check
'use strict';

var fs = require('fs');
var path = require('path');
var util = require('util');
var readfiles = require('node-readfiles');
var mdv = require('./index.js');

var argv = require('yargs')
	.usage('testRunner [options] [{path-to-specs}...]')
	.string('encoding')
	.alias('e','encoding')
	.default('encoding','utf8')
	.describe('encoding','encoding for input/output files')
	.string('fail')
	.describe('fail','path to specs expected to fail')
	.alias('f','fail')
	.boolean('quiet')
	.alias('q','quiet')
	.describe('quiet','do not show test passes on console, for CI')
	.boolean('stop')
	.alias('s','stop')
	.describe('stop','stop on first error')
	.count('verbose')
	.alias('v','verbose')
	.describe('verbose','increase verbosity')
	.help('h')
    .alias('h', 'help')
	.strict()
	.version(function() {
		return require('../package.json').version;
	})
	.argv;

var red = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[31m';
var green = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[32m';
var yellow = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[33;1m';
var normal = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[0m';

var pass = 0;
var fail = 0;
var failures = [];
var warnings = [];

var options = argv;

function checkOutput(output) {
	var result = true;
	for (var k in output) {
		if (typeof output[k] === 'number') {
			if (output[k] > 0) result = false;
		}
		if (Array.isArray(output[k])) {
			if (output[k].length > 0) result = false;
		}
	}
	return result;
}

function check(file,force,expectFailure) {
	var result = false;
	options.expectFailure = expectFailure;
	options.source = file;

	var components = file.split(path.sep);
	var name = components[components.length-1];

	if ((name.endsWith('.md')) || force) {

		var srcStr = fs.readFileSync(path.resolve(file),options.encoding);
		var output = {failed:true};
		var result = false;
		try {
			output = mdv.validate(srcStr,options);
			result = checkOutput(output);
		}
		catch (ex) {
			console.log(red+'mdv threw an error: '+ex.message);
			warnings.push('mdv threw '+options.source);
			result = false;
		}

		console.log((result ? green : red)+file);
		if (!result) {
			console.log(util.inspect(output));
		}
		if (expectFailure) result = !result;
		if (result) {
			pass++;
		}
		else {
			fail++;
		}

	}
	else {
		result = true;
	}
	return result;
}

function processPathSpec(pathspec,expectFailure) {
	pathspec = path.resolve(pathspec);
	var stats = fs.statSync(pathspec);
	if (stats.isFile()) {
		check(pathspec,true,expectFailure);
	}
	else {
		readfiles(pathspec, {readContents: false, filenameFormat: readfiles.FULL_PATH}, function (err) {
			if (err) console.log(util.inspect(err));
		})
		.then(files => {
			files = files.sort(function(a,b){
				if (a<b) return +1;
				if (a>b) return -1;
				return 0;
			});
			for (var file of files) {
				check(file,false,expectFailure);
			}
		})
		.catch(err => {
			console.log(util.inspect(err));
		});
	}
}

process.exitCode = 1;
console.log('Gathering...');
if ((!argv._.length) && (!argv.fail)) {
	argv._.push('test/pass/');
}
for (let pathspec of argv._) {
	processPathSpec(pathspec,false);
}
if (argv.fail) {
	if (!Array.isArray(argv.fail)) argv.fail = [argv.fail];
	for (let pathspec of argv.fail) {
		processPathSpec(pathspec,true);
	}
}

process.on('exit', function() {
	if (warnings.length) {
		warnings.sort();
		console.log(normal+'\nWarnings:'+yellow);
		for (var w in warnings) {
			console.log(warnings[w]);
		}
	}
	if (failures.length) {
		failures.sort();
		console.log(normal+'\nFailures:'+red);
		for (var f in failures) {
			console.log(failures[f]);
		}
	}
	console.log(normal);
	console.log('Tests: %s passing, %s failing, %s warnings', pass, fail, warnings.length);
	process.exitCode = ((fail === 0) && (pass > 0)) ? 0 : 1;
});
