// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';

var util = require('util');
var assert = require('assert');
var semver = require('semver');
var PluginBase = require('../plugin-base');
var g = require('../globalize');

module.exports = function(options) {
  return new Application(options);
};

function Application(options) {
  PluginBase.call(this, options, 'application', 'config');
}

util.inherits(Application, PluginBase);

function assertLoopBackVersion(app) {
  var RANGE = '2.x || 3.x';

  var loopback = app.loopback;
  // remove any pre-release tag from the version string,
  // because semver has special treatment of pre-release versions,
  // while loopback-boot treats pre-releases the same way as regular versions
  var version = (loopback.version || '1.0.0').replace(/-.*$/, '');
  if (!semver.satisfies(version, RANGE)) {
    var msg = g.f(
      'The `app` is powered by an incompatible loopback version %s. ' +
      'Supported versions: %s',
      loopback.version || '(unknown)',
      RANGE
    );
    throw new Error(msg);
  }
}

function setEnv(app, env) {
  if (env !== undefined)
    app.set('env', env);
}

function setHost(app, appConfig) {
  // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
  var host =
    process.env.npm_config_host ||
    process.env.OPENSHIFT_SLS_IP ||
    process.env.OPENSHIFT_NODEJS_IP ||
    process.env.HOST ||
    process.env.VCAP_APP_HOST ||
    appConfig.host ||
    process.env.npm_package_config_host ||
    app.get('host');

  if (host !== undefined) {
    assert(typeof host === 'string', 'app.host must be a string');
    app.set('host', host);
  }
}

function setPort(app, appConfig) {
  // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
  var port = find([
    process.env.npm_config_port,
    process.env.OPENSHIFT_SLS_PORT,
    process.env.OPENSHIFT_NODEJS_PORT,
    process.env.PORT,
    process.env.VCAP_APP_PORT,
    appConfig.port,
    process.env.npm_package_config_port,
    app.get('port'),
    3000,
  ], function(p) {
    return p != null;
  });

  if (port !== undefined) {
    var portType = typeof port;
    assert(portType === 'string' || portType === 'number',
      'app.port must be a string or number');
    app.set('port', port);
  }
}

function find(array, predicate) {
  return array.filter(predicate)[0];
}

function setApiRoot(app, appConfig) {
  var restApiRoot =
    appConfig.restApiRoot ||
    app.get('restApiRoot') ||
    '/api';

  assert(restApiRoot !== undefined, 'app.restBasePath is required');
  assert(typeof restApiRoot === 'string',
    'app.restApiRoot must be a string');
  assert(/^\//.test(restApiRoot),
    'app.restApiRoot must start with "/"');
  app.set('restApiRoot', restApiRoot);
}

function applyAppConfig(app, appConfig) {
  for (var configKey in appConfig) {
    var cur = app.get(configKey);
    if (cur === undefined || cur === null) {
      app.set(configKey, appConfig[configKey]);
    }
  }
}

Application.prototype.starting = function(context) {
  var app = context.app;
  app.booting = true;
  assertLoopBackVersion(app);

  var appConfig = context.instructions.application;
  setEnv(app, context.instructions.env || this.options.env);
  setHost(app, appConfig);
  setPort(app, appConfig);
  setApiRoot(app, appConfig);
  applyAppConfig(app, appConfig);
};

Application.prototype.started = function(context, done) {
  var app = context.app;
  app.booting = false;
  process.nextTick(function() {
    app.emit('booted');
    done();
  });
};