'use strict';

var async = require('async');
var nconf = require('nconf');
var url = require('url');
var winston = require('winston');

var start = module.exports;

start.start = function () {
	var db = require('./database');

	setupConfigs();

	printStartupInfo();

	addProcessHandlers();

	async.waterfall([
		function (next) {
			db.init(next);
		},
		function (next) {
			var meta = require('./meta');
			async.parallel([
				async.apply(db.checkCompatibility),
				async.apply(meta.configs.init),
				function (next) {
					if (nconf.get('dep-check') === undefined || nconf.get('dep-check') !== false) {
						meta.dependencies.check(next);
					} else {
						winston.warn('[init] Dependency checking skipped!');
						setImmediate(next);
					}
				},
				function (next) {
					require('./upgrade').check(next);
				}
			], function (err) {
				next(err);
			});
		},
		function(next) {
			db.initSessionStore(next);
		},
		function (next) {
			var webserver = require('./webserver');
			require('./socket.io').init(webserver.server);

			if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) {
				require('./notifications').init();
				require('./user').startJobs();
			}

			webserver.listen(next);
		}
	], function (err) {
		if (err) {
			switch(err.message) {
				case 'schema-out-of-date':
					winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
					winston.warn('    ./nodebb upgrade');
					break;
				case 'dependencies-out-of-date':
					winston.warn('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:');
					winston.warn('    ./nodebb upgrade');
					break;
				case 'dependencies-missing':
					winston.warn('One or more of NodeBB\'s dependent packages are missing. Please run the following command to update them:');
					winston.warn('    ./nodebb upgrade');
					break;
				default:
					winston.error(err);
					break;
			}

			// Either way, bad stuff happened. Abort start.
			process.exit();
		}

		if (process.send) {
			process.send({
				action: 'listening'
			});
		}
	});
};

function setupConfigs() {
	// nconf defaults, if not set in config
	if (!nconf.get('upload_path')) {
		nconf.set('upload_path', '/public/uploads');
	}
	if (!nconf.get('sessionKey')) {
		nconf.set('sessionKey', 'express.sid');
	}
	// Parse out the relative_url and other goodies from the configured URL
	var urlObject = url.parse(nconf.get('url'));
	var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : '';
	nconf.set('base_url', urlObject.protocol + '//' + urlObject.host);
	nconf.set('secure', urlObject.protocol === 'https:');
	nconf.set('use_port', !!urlObject.port);
	nconf.set('relative_path', relativePath);
	nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
	nconf.set('upload_url', nconf.get('upload_path').replace(/^\/public/, ''));
}

function printStartupInfo() {
	if (nconf.get('isPrimary') === 'true') {
		winston.info('Time: %s', (new Date()).toString());
		winston.info('Initializing NodeBB v%s', nconf.get('version'));

		var host = nconf.get(nconf.get('database') + ':host');
		var storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : '';

		winston.verbose('* using %s store %s', nconf.get('database'), storeLocation);
		winston.verbose('* using themes stored in: %s', nconf.get('themes_path'));
	}
}

function addProcessHandlers() {
	process.on('SIGTERM', shutdown);
	process.on('SIGINT', shutdown);
	process.on('SIGHUP', restart);
	process.on('message', function (message) {
		if (typeof message !== 'object') {
			return;
		}
		var meta = require('./meta');

		switch (message.action) {
			case 'reload':
				meta.reload();
			break;
		}
	});

	process.on('uncaughtException', function (err) {
		winston.error(err);

		require('./meta').js.killMinifier();
		shutdown(1);
	});
}

function restart() {
	if (process.send) {
		winston.info('[app] Restarting...');
		process.send({
			action: 'restart'
		});
	} else {
		winston.error('[app] Could not restart server. Shutting down.');
		shutdown(1);
	}
}

function shutdown(code) {
	winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.');
	require('./database').close();
	winston.info('[app] Database connection closed.');
	require('./webserver').server.close();
	winston.info('[app] Web server closed to connections.');

	winston.info('[app] Shutdown complete.');
	process.exit(code || 0);
}