'use strict';

process.env.NODE_CONFIG_DIR = `${__dirname}/config`;

const async = require('async');
const fs = require('fs');

//const Discord = require("discord.js");
//const client = new Discord.Client();
const db = require('./logic/db');
const app = require('./logic/app');
const mail = require('./logic/mail');
const api = require('./logic/api');
const io = require('./logic/io');
const stations = require('./logic/stations');
const songs = require('./logic/songs');
const playlists = require('./logic/playlists');
const cache = require('./logic/cache');
const discord = require('./logic/discord');
const notifications = require('./logic/notifications');
const punishments = require('./logic/punishments');
const logger = require('./logic/logger');
const tasks = require('./logic/tasks');
const config = require('config');

let currentComponent;
let initializedComponents = [];
let lockdownB = false;

process.on('uncaughtException', err => {
	if (lockdownB || err.code === 'ECONNREFUSED' || err.code === 'UNCERTAIN_STATE') return;
	console.log(`UNCAUGHT EXCEPTION: ${err.stack}`);
});

const getError = (err) => {
	let error = 'An error occurred.';
	if (typeof err === "string") error = err;
	else if (err.message) {
		if (err.message !== 'Validation failed') error = err.message;
		else error = err.errors[Object.keys(err.errors)].message;
	}
	return error;
};

function lockdown() {
	if (lockdownB) return;
	lockdownB = true;
	initializedComponents.forEach((component) => {
		component._lockdown();
	});
	console.log("Backend locked down.");
}

function errorCb(message, err, component) {
	err = getError(err);
	lockdown();
	discord.sendAdminAlertMessage(message, "#FF0000", message, true, [{name: "Error:", value: err, inline: false}, {name: "Component:", value: component, inline: true}]); //TODO Maybe due to lockdown this won't work, and what if the Discord module was the one that failed?
}

function moduleStartFunction() {
	logger.info("MODULE_START", `Starting to initialize component '${currentComponent}'`);
}

async.waterfall([

	// setup our Discord module
	(next) => {
		currentComponent = 'Discord';
		moduleStartFunction();
		discord.init(config.get('apis.discord').token, config.get('apis.discord').loggingChannel, errorCb, () => {
			next();
		});
	},

	// setup our Redis cache
	(next) => {
		currentComponent = 'Cache';
		moduleStartFunction();
		cache.init(config.get('redis').url, config.get('redis').password, errorCb, () => {
			next();
		});
	},

	// setup our MongoDB database
	(next) => {
		initializedComponents.push(cache);
		currentComponent = 'DB';
		moduleStartFunction();
		db.init(config.get("mongo").url, errorCb, next);
	},

	// setup the express server
	(next) => {
		initializedComponents.push(db);
		currentComponent = 'App';
		moduleStartFunction();
		app.init(next);
	},

	// setup the mail
	(next) => {
		initializedComponents.push(app);
		currentComponent = 'Mail';
		moduleStartFunction();
		mail.init(next);
	},

	// setup the socket.io server (all client / server communication is done over this)
	(next) => {
		initializedComponents.push(mail);
		currentComponent = 'IO';
		moduleStartFunction();
		io.init(next);
	},

	// setup the punishment system
	(next) => {
		initializedComponents.push(io);
		currentComponent = 'Punishments';
		moduleStartFunction();
		punishments.init(next);
	},

	// setup the notifications
	(next) => {
		initializedComponents.push(punishments);
		currentComponent = 'Notifications';
		moduleStartFunction();
		notifications.init(config.get('redis').url, config.get('redis').password, errorCb, next);
	},

	// setup the stations
	(next) => {
		initializedComponents.push(notifications);
		currentComponent = 'Stations';
		moduleStartFunction();
		stations.init(next)
	},

	// setup the songs
	(next) => {
		initializedComponents.push(stations);
		currentComponent = 'Songs';
		moduleStartFunction();
		songs.init(next)
	},

	// setup the playlists
	(next) => {
		initializedComponents.push(songs);
		currentComponent = 'Playlists';
		moduleStartFunction();
		playlists.init(next)
	},

	// setup the API
	(next) => {
		initializedComponents.push(playlists);
		currentComponent = 'API';
		moduleStartFunction();
		api.init(next)
	},

	// setup the logger
	(next) => {
		initializedComponents.push(api);
		currentComponent = 'Logger';
		moduleStartFunction();
		logger.init(next)
	},

	// setup the tasks system
	(next) => {
		initializedComponents.push(logger);
		currentComponent = 'Tasks';
		moduleStartFunction();
		tasks.init(next)
	},

	// setup the frontend for local setups
	(next) => {
		initializedComponents.push(tasks);
		currentComponent = 'Windows';
		moduleStartFunction();
		if (!config.get("isDocker") && !(config.get("mode") === "development" || config.get("mode") === "dev")) {
			const express = require('express');
			const app = express();
			app.listen(config.get("frontendPort"));
			const rootDir = __dirname.substr(0, __dirname.lastIndexOf("backend")) + "frontend/dist/build";

			app.use(express.static(rootDir, {
				setHeaders: function(res, path) {
					if (path.indexOf('.html') !== -1) res.setHeader('Cache-Control', 'public, max-age=0');
					else res.setHeader('Cache-Control', 'public, max-age=2628000');
				}
			}));

			app.get("/*", (req, res) => {
				res.sendFile(`${rootDir}/index.html`);
			});
		}
		if (lockdownB) return;
		next();
	}
], (err) => {
	if (err && err !== true) {
		lockdown();
		discord.sendAdminAlertMessage("An error occurred while initializing the backend server.", "#FF0000", "Startup error", true, [{name: "Error:", value: err, inline: false}, {name: "Component:", value: currentComponent, inline: true}]);
		console.error('An error occurred while initializing the backend server');
	} else {
		discord.sendAdminAlertMessage("The backend server started successfully.", "#00AA00", "Startup", false, []);
		console.info('Backend server has been successfully started');
	}
});