// Original file created by Prof.Dr. Matthias Hopf

/**
 * Express based http & https server
 *
 * Requires express >= 4
 */
var common 	  = require('./server/common'),
    //admin     = require ('./server/admin'),
	authorize = require('./server/authorization'),
	dbs       = require('./server/dbs');
    //files     = require ('./server/files');

const fs          = common.fs, // file sync, read certificates
      http        = common.http, // http handler
      https       = require('https'), // https handler
      express     = require('express'), // node server framework
      session     = require('express-session'), // session management (security)
      morgan      = require('morgan'), // logger
      mong        = common.mongoose, // mongoose
      serveFavicon = require('serve-favicon'), // provide favicon
      bodyParser  = require('body-parser'), // post request bodyparser
      MongoStore  = require('connect-mongo')(session); // use mongodb as session storage

var app = express();

/*
 * Init
 */
common    .init();
dbs       .init (common);
authorize .init(common);
//admin    .init (common);
//files    .init (common);
mong.Promise = global.Promise;

/*
 * Security
 *
 * TODO: Install helmet
 * https://expressjs.com/de/advanced/best-practice-security.html
 *
 * (Disable Header information: Powerd by Express)
 * -> Information disclosure
 */
app.disable('x-powered-by');


/*
 * Route Control
 */
// Session Management
app.set('trust proxy', 1) // trust first proxy, neccessary for cookie secure: true flag
app.use(session({
	secret: 'ahhgylhuvh', // caesar(3) 2 letter surname
	saveUninitialized: false, // don't create session until something stored
	resave: false, //don't save session if unmodified
	cookie: {
		maxAge: 30 * 24 * 3600 * 1000, // TODO: ttl for session as well (Store)
		secure: true, // true for https only (since our app works only with https)
	},
	name: 'om.sid',
	store: new MongoStore({
		mongooseConnection: mong.connection,
		ttl: 30 * 24 * 3600,
	}), // mongoose + connect-mongo
	//store: new MemoryStore ({checkPeriod: 24*3600*1000}),   // memorystore
}));

// Favicon for Desktop
app.use (serveFavicon(__dirname + '/public/img/favicon.ico'));

// Minimal Logging
//app.use (morgan ('dev'));
// Advanced Logging
morgan.token('user', function (req, res) {
	return (req.session && req.session.user) || '-';
});
morgan.token('userColored', function (req, res) {
	var color = 0;
	if (req.session && req.session.roles)
		color = req.session.roles.admin ? 31 // red
               : req.session.roles.user ? 34 // blue
               : 0;                          // no color
	return '\x1b[' + color + 'm' + ((req.session && req.session.user) || '-') + '\x1b[0m';
});
morgan.token('statusColored', function (req, res) {
	var color = res.statusCode >= 500 ? 31 // red
		: res.statusCode >= 400 ? 33 // yellow
		: res.statusCode >= 300 ? 36 // cyan
		: res.statusCode >= 200 ? 32 // green
		: 0;                         // no color
	return '\x1b[' + color + 'm' + (res.headersSent ? res.statusCode : '-') + '\x1b[0m';
});
app.use(morgan(':date[iso] :statusColored :method :url :userColored :response-time ms :res[content-length]'));

// BodyParser
// Returns middleware that only parses json bodies.
// (https://www.npmjs.com/package/body-parser#bodyparserjsonoptions)
app.use(bodyParser.json());
// Returns middleware that only parses urlencoded bodies
// with qs library (https://www.npmjs.com/package/qs#readme)
app.use(bodyParser.urlencoded({
	extended: true
}));

// API
var api_routes = express.Router(); // express app-object routing
app.use('/api', api_routes);

// Static Files - Allow access to 'public' folder
app.use(express.static(__dirname + '/public'));

// Other stuff is NOT authorized unless logged in
app.use (authorize.genCheckAuthorized ('user'));

// No error so far? Then it's a 404!
app.use(function (req, res, next) {
	next(common.genError(404, req.url));
});
//app.use (routes.errorHandler (true)); /* true: show stack traces */

/*
 * API
 */
// API allowed for all
api_routes.post('/login', authorize.login);
api_routes.post('/logout', authorize.logout);

// Validate all other API calls
// TODO - Validate API calls
//api_routes.use(authorize.genCheckAuthorized('user'));

// Add API routes
function addRoutes(r) {
	for (var e in r.routes) {
		var route = '/' + e + (r.routes[e].params ? "/" + r.routes[e].params : "");
		var log = "Adding routes for " + route + ":";

		var isAuth = r.routes[e].auth || r.auth;
		if (isAuth) {
		    log += " [auth]";
		    api_routes.use (route, function (req, res, next) {
		        if (! auth(req))
		            return next (common.genError (403, "Unauthorized"));
		        next ();
		    });
		}

		var role = r.routes[e].role || r.role;
		if (role) {
		    log += " [role:"+role+"]";
		    api_routes.use (route, authorize.genCheckAuthorized (role));
		}

		const methods = ["get", "post", "put", "delete"];
		for (var m in methods) {
			if (r.routes[e][methods[m]]) {
				log += " " + methods[m];
				api_routes[methods[m]](route, r.routes[e][methods[m]]);
			}
		}
		console.log(log);
	}
}

addRoutes(dbs);
//addRoutes (admin);
//addRoutes (files);

/*
 * Servers
 */
http.createServer(app).listen(common.config.httpPort, function () {
	console.log("Express http server listening on port " +
                common.config.httpPort);
});

/*
 * SSL certificates
 *
 * Keys + Certificate in current dir (not servable!)
 * to create (self-signed) SSL certs:
 *
 * openssl genrsa -out privatekey.pem 1024
 * openssl req -new -key privatekey.pem -out certrequest.csr
 * openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
 * rm certrequest.csr
 */
// >>>>>>>>>>>>>>>>>>>>>>>>>>>  SSL:  DISABLED  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/*
if (common.config.httpsPort) {
	var options;
	try {
		try {
			// In case it's a real certificate: add CA chain cersts (TODO: use array if required)
			var ca = fs.readFileSync('keys/ca_cert.pem');
		} catch (e) {
			ca = undefined;
			console.log("Note: Can't read CA bundle: " + e);
		}
		if (ca != undefined) {
			options = {
				key: fs.readFileSync('keys/omkey.pem'),
				cert: fs.readFileSync('keys/certificate.pem'),
				ca: ca
			};
			https.createServer(options, app).listen(common.config.httpsPort, function () {
				console.log("Express https server listening on port " + common.config.httpsPort);
			});
		}
	} catch (e) {
		console.log("Note: Can't read SSL keys/certs: " + e + "\nDisabling https server");
	}
} else {
	console.log("Note: https server disabled by config");
}

            common.mongoose.set('useCreateIndex', true);*/

/*
 * Uncaught Exceptions
 */
process.on("uncaughtException", function (err) {
	console.error("*** Uncaught Exception:");
	console.error(err.stack);
});
