/*!
 * Connect - logger borrowed for connect-notifo
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * Copyright(c) 2011 mape
 * MIT Licensed
 *
 * Tokens:
 *
 *  - `:req[header]` ex: `:req[Accept]`
 *  - `:res[header]` ex: `:res[Content-Length]`
 *  - `:http-version`
 *  - `:response-time`
 *  - `:remote-addr`
 *  - `:date`
 *  - `:method`
 *  - `:url`
 *  - `:referrer`
 *  - `:user-agent`
 *  - `:status`
 *
 * Formats:
 *
 *  Pre-defined formats:
 *
 *  - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
 *  - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'
 *  - `tiny` ':method :url :status :res[content-length] - :response-time ms'
 *  - `dev` concise output colored by response status for development use
 */

var Notifo = require('notifo');

exports = module.exports = function logger(auths, options) {
	if (!auths || Array.isArray(auths) && !auths.username && !options) {
		return function forward(req, res, next) {
			next();
		};
	}

	if (!Array.isArray(auths)) {
		auths = [auths];
	}

	var recievers = [];
	auths.forEach(function (auth) {
		recievers.push(new Notifo({
			'username': auth.username
			, 'secret': auth.secret
		}))
	});

	if (!options.filter) {
		options.filter = function (req, res, callback) {
			callback(null, true);
		};
	}
	if (!options.format) {
		options.format = function (req, res, callback) {
			callback(null, {
				'title': ':req[x-real-ip]  @ :req[host]'
				, 'message': 'short'
			});
		};
	}

	return function notifoSender(req, res, next) {
		options.filter(req, res, function (err, passed) {
			if (err || !passed) {
				return;
			}
			options.format(req, res, function (err, notificationData) {
				if (err) {
					return;
				}

				var titleFn = compile(exports[notificationData.title] || notificationData.title);
				var msgFn = compile(exports[notificationData.message] || notificationData.message);

				var msgObj = {
					'title': titleFn(exports, req, res)
					, 'msg': msgFn(exports, req, res)
				};

				recievers.forEach(function (notification) {
					notification.send(msgObj, function (err, response) {});
				});
			});
		});
		next();
	};
};

function compile(fmt) {
	fmt = fmt.replace(/"/g, '\\"');
	var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function (_, name, arg) {
		return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
	}) + '";'
	return new Function('tokens, req, res', js);
};

exports.token = function (name, fn) {
	exports[name] = fn;
	return this;
};

exports.format = function (name, str) {
	exports[name] = str;
	return this;
};

// default format
exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');

// short format
exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');

// tiny format
exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');

// request url
exports.token('url', function (req) { return req.originalUrl; });

// request method
exports.token('method', function (req) { return req.method; });

// response time in milliseconds
exports.token('response-time', function (req) { return new Date - req._startTime; });

// UTC date
exports.token('date', function () { return new Date().toUTCString(); });

// response status code
exports.token('status', function (req, res) { return res.statusCode; });

// normalized referrer
exports.token('referrer', function (req) { return req.headers['referer'] || req.headers['referrer']; });

// remote address
exports.token('remote-addr', function (req) { return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)); });

// HTTP version
exports.token('http-version', function (req) { return req.httpVersionMajor + '.' + req.httpVersionMinor; });

// UA string
exports.token('user-agent', function (req) { return req.headers['user-agent']; });

// request header
exports.token('req', function (req, res, field) { return req.headers[field.toLowerCase()]; });

// response header
exports.token('res', function (req, res, field) { return (res._headers || {})[field.toLowerCase()]; });