/*
 * Copyright (c) 2016-present, Parse, LLC
 * All rights reserved.
 *
 * This source code is licensed under the license found in the LICENSE file in
 * the root directory of this source tree.
 */
// Command line tool for npm start
'use strict'
const path = require('path');
const fs = require('fs');
const express = require('express');
const parseDashboard = require('./app');
const CLIHelper = require('./CLIHelper.js');

const program = require('commander');
program.option('--appId [appId]', 'the app Id of the app you would like to manage.');
program.option('--masterKey [masterKey]', 'the master key of the app you would like to manage.');
program.option('--serverURL [serverURL]', 'the server url of the app you would like to manage.');
program.option('--graphQLServerURL [graphQLServerURL]', 'the GraphQL server url of the app you would like to manage.');
program.option('--dev', 'Enable development mode. This will disable authentication and allow non HTTPS connections. DO NOT ENABLE IN PRODUCTION SERVERS');
program.option('--appName [appName]', 'the name of the app you would like to manage. Optional.');
program.option('--config [config]', 'the path to the configuration file');
program.option('--host [host]', 'the host to run parse-dashboard');
program.option('--port [port]', 'the port to run parse-dashboard');
program.option('--mountPath [mountPath]', 'the mount path to run parse-dashboard');
program.option('--allowInsecureHTTP [allowInsecureHTTP]', 'set this flag when you are running the dashboard behind an HTTPS load balancer or proxy with early SSL termination.');
program.option('--sslKey [sslKey]', 'the path to the SSL private key.');
program.option('--sslCert [sslCert]', 'the path to the SSL certificate.');
program.option('--trustProxy [trustProxy]', 'set this flag when you are behind a front-facing proxy, such as when hosting on Heroku.  Uses X-Forwarded-* headers to determine the client\'s connection and IP address.');
program.option('--cookieSessionSecret [cookieSessionSecret]', 'set the cookie session secret, defaults to a random string. You should set that value if you want sessions to work across multiple server, or across restarts');
program.option('--createUser', 'helper tool to allow you to generate secure user passwords and secrets. Use this on trusted devices only.');
program.option('--createMFA', 'helper tool to allow you to generate multi-factor authentication secrets.');

program.parse(process.argv);
const options = program.opts();

for (const key in options) {
  const func = CLIHelper[key];
  if (func && typeof func === 'function') {
    func();
    return;
  }
}

const host = options.host || process.env.HOST || '0.0.0.0';
const port = options.port || process.env.PORT || 4040;
const mountPath = options.mountPath || process.env.MOUNT_PATH || '/';
const allowInsecureHTTP = options.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP;
const cookieSessionSecret = options.cookieSessionSecret || process.env.PARSE_DASHBOARD_COOKIE_SESSION_SECRET;
const trustProxy = options.trustProxy || process.env.PARSE_DASHBOARD_TRUST_PROXY;
const dev = options.dev;

if (trustProxy && allowInsecureHTTP) {
  console.log('Set only trustProxy *or* allowInsecureHTTP, not both.  Only one is needed to handle being behind a proxy.');
  process.exit(-1);
}

let explicitConfigFileProvided = !!options.config;
let configFile = null;
let configFromCLI = null;
let configServerURL = options.serverURL || process.env.PARSE_DASHBOARD_SERVER_URL;
let configGraphQLServerURL = options.graphQLServerURL || process.env.PARSE_DASHBOARD_GRAPHQL_SERVER_URL;
let configMasterKey = options.masterKey || process.env.PARSE_DASHBOARD_MASTER_KEY;
let configAppId = options.appId || process.env.PARSE_DASHBOARD_APP_ID;
let configAppName = options.appName || process.env.PARSE_DASHBOARD_APP_NAME;
let configUserId = options.userId || process.env.PARSE_DASHBOARD_USER_ID;
let configUserPassword = options.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD;
let configSSLKey = options.sslKey || process.env.PARSE_DASHBOARD_SSL_KEY;
let configSSLCert = options.sslCert || process.env.PARSE_DASHBOARD_SSL_CERT;

function handleSIGs(server) {
  const signals = {
    'SIGINT': 2,
    'SIGTERM': 15
  };
  function shutdown(signal, value) {
    server.close(function () {
      console.log('server stopped by ' + signal);
      process.exit(128 + value);
    });
  }
  Object.keys(signals).forEach(function (signal) {
    process.on(signal, function () {
      shutdown(signal, signals[signal]);
    });
  });
}

if (!options.config && !process.env.PARSE_DASHBOARD_CONFIG) {
  if (configServerURL && configMasterKey && configAppId) {
    configFromCLI = {
      data: {
        apps: [
          {
            appId: configAppId,
            serverURL: configServerURL,
            masterKey: configMasterKey,
            appName: configAppName,
          },
        ]
      }
    };
    if (configGraphQLServerURL) {
      configFromCLI.data.apps[0].graphQLServerURL = configGraphQLServerURL;
    }
    if (configUserId && configUserPassword) {
      configFromCLI.data.users = [
        {
          user: configUserId,
          pass: configUserPassword,
        }
      ];
    }
  } else if (!configServerURL && !configMasterKey && !configAppName) {
    configFile = path.join(__dirname, 'parse-dashboard-config.json');
  }
} else if (!options.config && process.env.PARSE_DASHBOARD_CONFIG) {
  configFromCLI = {
    data: JSON.parse(process.env.PARSE_DASHBOARD_CONFIG)
  };
} else {
  configFile = options.config;
  if (options.appId || options.serverURL || options.masterKey || options.appName || options.graphQLServerURL) {
    console.log('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.');
    process.exit(3);
  }
}

let config = null;
let configFilePath = null;
if (configFile) {
  try {
    config = {
      data: JSON.parse(fs.readFileSync(configFile, 'utf8'))
    };
    configFilePath = path.dirname(configFile);
  } catch (error) {
    if (error instanceof SyntaxError) {
      console.log('Your config file contains invalid JSON. Exiting.');
      process.exit(1);
    } else if (error.code === 'ENOENT') {
      if (explicitConfigFileProvided) {
        console.log('Your config file is missing. Exiting.');
        process.exit(2);
      } else {
        console.log('You must provide either a config file or required CLI options (app ID, Master Key, and server URL); not both.');
        process.exit(3);
      }
    } else {
      console.log('There was a problem with your config. Exiting.');
      process.exit(-1);
    }
  }
} else if (configFromCLI) {
  config = configFromCLI;
} else {
  //Failed to load default config file.
  console.log('You must provide either a config file or an app ID, Master Key, and server URL. See parse-dashboard --help for details.');
  process.exit(4);
}

config.data.apps.forEach(app => {
  if (!app.appName) {
    app.appName = app.appId;
  }
});

if (config.data.iconsFolder && configFilePath) {
  config.data.iconsFolder = path.join(configFilePath, config.data.iconsFolder);
}

const app = express();

if (allowInsecureHTTP || trustProxy || dev) app.enable('trust proxy');

config.data.trustProxy = trustProxy;
let dashboardOptions = { allowInsecureHTTP, cookieSessionSecret, dev };
app.use(mountPath, parseDashboard(config.data, dashboardOptions));
let server;
if(!configSSLKey || !configSSLCert){
  // Start the server.
  server = app.listen(port, host, function () {
    console.log(`The dashboard is now available at http://${server.address().address}:${server.address().port}${mountPath}`);
  });
} else {
  // Start the server using SSL.
  var privateKey = fs.readFileSync(configSSLKey);
  var certificate = fs.readFileSync(configSSLCert);

  server = require('https').createServer({
    key: privateKey,
    cert: certificate
  }, app).listen(port, host, function () {
    console.log(`The dashboard is now available at https://${server.address().address}:${server.address().port}${mountPath}`);
  });
}
handleSIGs(server);
