/*
 * Start module for CrossMe Licensing API Server
 * Created by: Michael Pastushkov <michael@pastushkov.com>
 */

'use strict';

const VERSION = "1.0";
const NAME = 'CatGPT';

const express = require('express');
const compression = require('compression');
const cors = require('cors');
const cluster = require('cluster'); 
const os = require('os');
const { I18n } = require('i18n');
const cookieParser = require('cookie-parser');

const utils = require('./src/utils/utils');
//const dal = require('./src/utils/dal');
//const housekeeping = require('./src/common/housekeeping');

const app = express();
const config = require('config');

// Variations between Admin and API
let name, port, host, routes
if (process.argv[2] != 'admin') {
    routes = require('./src/api/routes');
    name = `${NAME} API Server`;
    port = config.get('server_api.port');
    host = config.get('server_api.host');
} else {
    routes = require('./src/admin/routes');
    name = `${NAME} Admin Server`;
    port = config.get('server_admin.port');
    host = config.get('server_admin.host');
}

// Clusterization
if (cluster.isPrimary)
    utils.log(`== ${name}`);

if (config.server_api.cluster_enabled) {
    let number_of_workers = config.server_api.worker_threads || os.cpus.length || 2;
    if (cluster.isPrimary) {
        utils.log(`Master thread starts with ${number_of_workers} workers`);
        housekeeping.init();
        for (let i = 0; i < number_of_workers; i++)
            cluster.fork();
        return;
    } 
}

// Needed for catching exceptions in async functions
// !! MUST be first in the middleware list !!
require('express-async-errors');

app.use(compression({ filter: false })); // this is just to provide res.flush with no real compression

// Disable caching
app.disable('etag');

// Localization engine init
const i18n = new I18n({
    locales: config.locales,
    directory: 'locales',
    queryParameter: 'lang', // query parameter to switch locale (ie. /home?lang=ch) - defaults to NULL
    register: 'global',
})

// To provide localization during request processing
// via the function t (attached to res) 
app.use(function (req, res, next) {
    i18n.init(req, res);
    res.t = function () {
        if (arguments[0] == '__rtl') { // page orientations right-to-left languages
            let loc = res.getLocale();
            return ['ar', 'fa', 'he'].indexOf(loc) >= 0 ? 'rtl' : 'ltr';
        }
        return i18n.__.apply(req, arguments);
    };
    next();
});

// Other middleware
app.use((req, res, next) => {
    if (req.method === 'POST' && !req.headers['content-type']) {
        req.headers['content-type'] = 'text/plain';
    }
    next();
});

app.use(cors());
app.use('/static', express.static('static'));
app.use('/locales', express.static('locales'));
app.use(express.urlencoded({ extended: true, limit: '200mb', parameterLimit: 1000000 }));
app.use(express.json({ limit: '200mb' }));
app.use(express.text({ type: 'text/plain' }));
app.use(cookieParser());

app.get('/', (req, res) => {
  res.redirect('/static/www');
});

// Initialize some modules
// if (cluster.isPrimary)
//     housekeeping.init();

// Centralized request tracking 
app.use(function (req, res, next) {
    let ip = utils.get_ip(req);
    utils.log(`${ip} ${req.method} ${req.url}]`);
    utils.log(req.headers, 6);
    if (!utils.is_empty(req.body)) {
        if (req.body instanceof Buffer)
            utils.log(req.body.toString(), 4);
        else
            utils.log(req.body, 4);
    }
    res.on("finish", () => {
        let details = res.details || '';
        utils.log(`${ip} [${res.statusCode}] ${details}`);
    });
    next();
});

// Initialize routing (it needs to be here!!)
// dal.init().then(() => { 

    routes.init(app);

    // Centralized exception handling (this must be after routes!)
    app.use(function (err, req, res, next) {
        utils.log_error(err);
        let code = 500; 
        let message = 'Server error';
        if (!Array.isArray(err)) {
            code = 500;
            message = err.toString();
        } else {
            code = err[0] || 500;
            message = err[1] || err.message || err.toString();
        }
        res.status(code);
        res.send({ error: message });
    });
//});

// Server start
app.listen(port, host, () => {
    utils.log(`Process ${process.pid} listening on ${host}:${port} ... `);
});

module.exports.version = VERSION;
module.exports.name = name;