/**
 * A minimalist mock-up of the ASAQC API, used
 * for testing.
 *
 * Use the following environment variables to
 * configure its behaviour:
 *
 * HTTP_PORT Port to listen to. Defaults to 3077.
 *
 * HTTPS_PEM Combined public and private parts of
 * a TLS certificate to use. If provided, an HTTPS
 * server will be started. Alternatively, the user
 * may provide separate files (see below).
 *
 * HTTPS_CERT Public certificate to use in HTTPS
 * mode.
 *
 * HTTPS_KEY Private key to use along `HTTPS_CERT`
 * in HTTPS mode. Note that both must be provided.
 *
 * HTTPS_CA Public certificate of a certificate
 * authority or the public part of a client certificate.
 * If provided, the client needs to authenticate with
 * a certificate signed by this authority, or with this
 * certifcate itself, if self-signed.
 *
 * See also ../index.js for other environment
 * variable options.
 *
 */
const http = require('http');
const https = require('https');
const fs = require('fs');
const express = require('express');

const mw = require('./middleware');

const app = express();
const verbose = process.env.NODE_ENV != 'test';

app.map = function(a, route){
	route = route || '';
	for (var key in a) {
		switch (typeof a[key]) {
			// { '/path': { ... }}
			case 'object':
				if (!Array.isArray(a[key])) {
					app.map(a[key], route + key);
					break;
				} // else drop through
				// get: function(){ ... }
			case 'function':
				if (verbose) console.log('%s %s', key, route);
				app[key](route, a[key]);
				break;
		}
	}
};

app.use(express.json({type: "application/json", strict: false, limit: '10mb'}));
app.use(express.urlencoded({ type: "application/x-www-form-urlencoded", extended: true }));
app.use(express.text({type: "text/*", limit: '10mb'}));
app.use((req, res, next) => {
	res.set("Access-Control-Allow-Origin", "*");
	res.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE");
	res.set("Access-Control-Allow-Headers", "Content-Type");
	next();
});

app.map({
	'/vt/v1/api/upload-file-encoded': {
		post: [ mw.authorisation, mw.post ]
	}
});

// Generic error handler. Stops stack dumps
// being sent to clients.
app.use(function (err, req, res, next) {
	const title = `HTTP backend error at ${req.method} ${req.originalUrl}`;
	const description = err.message;
	const message = err.message;
	const alert = {title, message, description, error: err};

	console.log("Error:", err);

	res.set("Content-Type", "application/json");
	if (err instanceof Error && err.name != "UnauthorizedError") {
		console.error(err.stack);
		res.set("Content-Type", "text/plain");
		res.status(500).send('General internal error');
	} else if (typeof err === 'string') {
		res.status(500).send({message: err});
	} else {
		res.status(err.status || 500).send({message: err.message || (err.inner && err.inner.message) || "Internal error"});
	}
});

app.disable('x-powered-by');
app.enable('trust proxy');
console.log('trust proxy is ' + (app.get('trust proxy')? 'on' : 'off'));

const addr = "127.0.0.1";

if (!module.parent) {
	var port = process.env.HTTP_PORT || 3000;
	var server = http.createServer(app).listen(port, addr);

	console.log('API started on port ' + port);
} else {
	app.start = function (port = 3000, path, locals={}) {

		app.locals = {...app.locals, ...locals};

		var root = app;
		if (path) {
			root = express();
			['x-powered-by', 'trust proxy'].forEach(k => root.set(k, app.get(k)));
			root.use(path, app);
		}

		if (process.env.HTTPS_PEM || (process.env.HTTPS_CERT && process.env.HTTPS_KEY)) {

			const options = {
				cert: fs.readFileSync(process.env.HTTPS_CERT || process.env.HTTPS_PEM),
				key: fs.readFileSync(process.env.HTTPS_KEY || process.env.HTTPS_PEM)
			};

			if (process.env.HTTPS_CA) {
				// Enable client certificate authentication
				options.requestCert = true;
				options.ca = fs.readFileSync(process.env.HTTPS_CA);
				options.rejectUnauthorized = true;
				console.log("TLS client authentication requested");
			}

			const server = https.createServer(options, root).listen(port, addr);

			if (server) {
				console.log(`TLS API started on port ${port}, prefix: ${path || "/"}`);
			}

			return server;
		} else {

			const server = http.createServer(root).listen(port, addr);

			if (server) {
				console.log(`API started on port ${port}, prefix: ${path || "/"}`);
			}

			return server;
		}

	}
	module.exports = app;
}
