/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/


 /**
  * This module provides an Express application to serve the Node-RED editor.
  *
  * It implements the Node-RED HTTP Admin API the Editor uses to interact
  * with the Node-RED runtime.
  *
  * @namespace @node-red/editor-api
  */

var express = require("express");
var bodyParser = require("body-parser");
var util = require('util');
var passport = require('passport');
var cors = require('cors');

var auth = require("./auth");
var apiUtil = require("./util");

var adminApp;
var server;
var editor;


/**
 * Initialise the module.
 * @param  {Object}     settings   The runtime settings
 * @param  {HTTPServer} _server     An instance of HTTP Server
 * @param  {Storage}    storage    An instance of Node-RED Storage
 * @param  {Runtime}    runtimeAPI An instance of Node-RED Runtime
 * @memberof @node-red/editor-api
 */
function init(settings,_server,storage,runtimeAPI) {
    server = _server;
    if (settings.httpAdminRoot !== false) {
        adminApp = express();

        var cors = require('cors');
        var corsHandler = cors({
           origin: "*",
           methods: "GET,PUT,POST,DELETE"
        });
        adminApp.use(corsHandler);

        if (settings.httpAdminMiddleware) {
            if (typeof settings.httpAdminMiddleware === "function" || Array.isArray(settings.httpAdminMiddleware)) {
                adminApp.use(settings.httpAdminMiddleware);
            }
        }

        var defaultServerSettings = {
            "x-powered-by": false
        }
        var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
        for (var eOption in serverSettings) {
            adminApp.set(eOption, serverSettings[eOption]);
        }

        auth.init(settings,storage);

        var maxApiRequestSize = settings.apiMaxLength || '5mb';
        adminApp.use(bodyParser.json({limit:maxApiRequestSize}));
        adminApp.use(bodyParser.urlencoded({limit:maxApiRequestSize,extended:true}));

        adminApp.get("/auth/login",auth.login,apiUtil.errorHandler);
        if (settings.adminAuth) {
            if (settings.adminAuth.type === "strategy") {
                auth.genericStrategy(adminApp,settings.adminAuth.strategy);
            } else if (settings.adminAuth.type === "credentials") {
                adminApp.use(passport.initialize());
                adminApp.post("/auth/token",
                    auth.ensureClientSecret,
                    auth.authenticateClient,
                    auth.getToken,
                    auth.errorHandler
                );
            } else if (settings.adminAuth.tokens) {
                adminApp.use(passport.initialize());
            }
            adminApp.post("/auth/revoke",auth.needsPermission(""),auth.revoke,apiUtil.errorHandler);
        }

        // Editor
        if (!settings.disableEditor) {
            editor = require("./editor");
            var editorApp = editor.init(server, settings, runtimeAPI);
            adminApp.use(editorApp);
        }

        if (settings.httpAdminCors) {
            var corsHandler = cors(settings.httpAdminCors);
            adminApp.use(corsHandler);
        }

        var adminApiApp = require("./admin").init(settings, runtimeAPI);
        adminApp.use(adminApiApp);
    } else {
        adminApp = null;
    }
}

/**
 * Start the module.
 * @return {Promise} resolves when the application is ready to handle requests
 * @memberof @node-red/editor-api
 */
async function start() {
    if (editor) {
        return editor.start();
    }
}

/**
 * Stop the module.
 * @return {Promise} resolves when the application is stopped
 * @memberof @node-red/editor-api
 */
async function stop() {
    if (editor) {
        editor.stop();
    }
}
module.exports = {
    init: init,
    start: start,
    stop: stop,

    /**
    * @memberof @node-red/editor-api
    * @mixes @node-red/editor-api_auth
    */
    auth: {
        needsPermission: auth.needsPermission
    },
    /**
     * The Express app used to serve the Node-RED Editor
     * @type ExpressApplication
     * @memberof @node-red/editor-api
     */
    get httpAdmin() { return adminApp; }
};