/** @module */
/**
 * Logger backend for linter.
 * This backend filters out logging messages with Logtype "lint/*" and
 * logs them (console, external service).
 */

'use strict';

var LintRequest = require('../mw/ApiRequest.js').LintRequest;
var Promise = require('../utils/promise.js');

/**
 * @class
 */
var LintLogger = function(env) {
	this._env = env;
	this.buffer = [];
};

LintLogger.prototype._lintError = function() {
	var env = this._env;
	var args = arguments;
	// Call this async since recursive sync calls to the logger are suppressed
	process.nextTick(function() {
		env.log.apply(env, args);
	});
};

/**
 * @method
 * @param {LogData} logData
 * @return {Promise}
 */
LintLogger.prototype.logLintOutput = Promise.async(function *(logData) {
	var env = this._env;
	var enabledBuffer;
	try {
		if (env.conf.parsoid.linting === true) {
			enabledBuffer = this.buffer;  // Everything is enabled
		} else if (Array.isArray(env.conf.parsoid.linting)) {
			enabledBuffer = this.buffer.filter(function(item) {
				return env.conf.parsoid.linting.indexOf(item.type) !== -1;
			});
		} else {
			console.assert(false, 'Why are we here? Linting is disabled.');
		}

		this.buffer = [];

		if (env.page.id % env.conf.parsoid.linter.apiSampling !== 0) {
			return;
		}

		// Skip linting if we cannot lint it
		if (!env.page.hasLintableContentModel()) {
			return;
		}

		if (!env.conf.parsoid.linter.sendAPI) {
			enabledBuffer.forEach(function(item) {
				// Call this async, since recursive sync calls to the logger
				// are suppressed.  This messes up the ordering, as you'd
				// expect, but since it's only for debugging it should be
				// acceptable.
				process.nextTick(function() {
					env.log('warn/lint/' + item.type, item);
				});
			});
			return;
		}

		if (!env.conf.wiki.linterEnabled) {
			// If it's not installed, we can't send a request,
			// so skip.
			return;
		}

		if (!env.pageWithOldid) {
			// We only want to send to the MW API if this was a request to
			// parse the full page.
			return;
		}

		// Only send the request if it the latest revision
		if (env.page.meta.revision.revid === env.page.latest) {
			try {
				var data = yield LintRequest.promise(env, JSON.stringify(enabledBuffer));
				if (data.error) { env.log('error/lint/api', data.error); }
			} catch (ee) {
				env.log('error/lint/api', ee);
			}
		}
	} catch (e) {
		this._lintError('error/lint/api', "Error in logLintOutput: ", e);
	}
});

/**
 * @method
 * @param {LogData} logData
 * @return {Promise}
 */
LintLogger.prototype.linterBackend = Promise.async(function *(logData) { // eslint-disable-line require-yield
	// Wrap in try-catch-finally so we can more accurately
	// pin errors to specific logging backends
	try {
		var lintObj = logData.logObject[0];

		var msg = {
			type: logData.logType.match(/lint\/(.*)/)[1],
			params: lintObj.params || {},
		};

		var dsr = lintObj.dsr;
		if (dsr) {
			msg.dsr = dsr;
			if (lintObj.templateInfo) {
				msg.templateInfo = lintObj.templateInfo;
			}

			this.buffer.push(msg);
		} else {
			this._lintError('error/lint', 'Missing DSR; msg=', msg);
		}
	} catch (e) {
		this._lintError('error/lint', 'Error in linterBackend: ', e);
	}
});

if (typeof module === "object") {
	module.exports.LintLogger = LintLogger;
}