/*
	Licensed Material - Property of IBM

	(C) Copyright 2015, 2016 IBM Corp.

	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.
*/


'use strict';

var Q = require('q');
var inquirer = require('inquirer');
var log = require('npmlog');

var strings = require('ibm-strings');
var errors = require('mdo-errors');
var cordovaSupport = require('mdo-cordova-support');
var MFPConfig = require('mfp-config-xml').mfpConfigXMLAPI;
var profileIO = require('./profileIO');
var externalizedStrings = require('./externalizedStrings');

var moduleName = 'getServerAndRuntime'; // - name for clarification during logging

/**
 * Obtains the default server profile, or a given profile if specified, and checks if a runtime is
 * available on the server.  If multiple runtimes are found the user will be prompted to select one.
 * It will also convert any hostname with the name localhost to a static ip.  If multiple ip addresses
 * are found the user will be prompted to select one.  See parameters below to change the default
 * behavior.
 *
 * Parameters:
 *
 *  serverProfile  // Optional String.  If specified will get the server profile with this name.  If
 *                    one is not found an error is returned.  Default is to use the default profile.
 *  runtime        // Optional String.  If specified will validate that the given runtime is available.
 *                    If it is not available an error is returned.  Default is to use the available runtime.
 *                    If more than one runtime is available user will be prompted to select one.
 *  password       // Optional String.  If specified will use the given password. Default is to use the
 *                    password found in the server profile.  If no password is found in the profile the
 *                    user will be prompted for one.
 *  useLocalHost   // Optional Boolean. If true, does not convert localhost to a static ip.  Default is false
 *                    and localhost will be converted to a static ip.  User will be prompted to select an ip
 *                    if multiple are found.
 *  neverPrompt    // Optional Boolean.  If true, will not prompt for runtime, password or static ip.  Will just
 *                    choose the first found if multiple ip addresses are found.  For password it simply uses
 *                    whatever is specified in the profile.  For runtime it uses the value in config.xml if multiple
 *                    are found.  Default is false.
 *
 *  Q.deferred.promise.
 *   On success, a JSON object containing the server and runtime details is returned
 *   On failure, an error is returned
 *
 * Example JSON ouput
 *
 *  {
 *      "runtime": "TestProject",
 *      "serverProfile": {
 *          "protocol": "http",
 *          "host": "server1.raleigh.ibm.com",
 *          "port": "9080",
 *          "login": "demo",
 *          "password": "demo",
 *          "contextRoot": "worklightadmin",
 *          "timeout": "30"
 *      }
 *  }
 */
module.exports = function (serverProfile, runtime, userPassword, useLocalHost, neverPrompt) {
  var deferred = Q.defer();
  var serverRuntimeContext = {};
  var noPrompt = global.MDO_CLI_NO_PROMPT || neverPrompt;

  log.level = global.MDO_CLI_LOG_LEVEL ? global.MDO_CLI_LOG_LEVEL : 'info';

  var createLocalProfile = serverProfile === undefined || serverProfile === 'local';
  profileIO.getServerProfiles(createLocalProfile, true).then(function (profiles) {
    if (serverProfile) {
      //validate that it exists
      if (profiles.servers[serverProfile] === undefined) {
        var profileNotFound = errors.getError('PROFILENOTFOUND', serverProfile);
        deferred.reject(profileNotFound);
        return deferred.promise;
      }
    }
    else {
      //validate that profiles.defaultServer exists
      if (profiles.defaultServer === undefined) {
        var noServerProfiles = errors.getError('MISSINGSERVERDEFINITION');
        deferred.reject(noServerProfiles);
        return deferred.promise;
      }
    }

    var profileName = serverProfile || profiles.defaultServer;
    var server = profiles.servers[profileName];
    profileIO.handleLocalHost(useLocalHost, profiles, profileName, noPrompt).then(
      function (localIPAddress) {
        if (localIPAddress) {
          server.host = localIPAddress;
        }
        var password = userPassword || server.password || '';
        inquirer.prompt([{
          name: 'password',
          message: externalizedStrings.whatServerPassword,
          type: 'password',
          when: function () {
            return password === '' && noPrompt !== true;
          }
        }], function (answers) {
          if (answers.password !== undefined) {
            password = answers.password;
          }
          else if (userPassword === undefined) {
            password = (new Buffer(server.password, 'base64')).toString();
          }
          server.password = password;
          console.log(externalizedStrings.serverCreateContactingServer);
          var options = {
            protocol: server.protocol,
            host: server.host,
            port: server.port,
            userName: server.login,
            userPassword: server.password,
            contextRoot: server.contextRoot,
            timeout: server.timeout
          };
          serverRuntimeContext.serverProfile = server;
          getRuntimes(profileName, options).then(function (runtimes) {
            if (runtimes !== undefined && runtimes.length > 0) {
              if (runtime === undefined) {
                if (runtimes.length > 1) {
                  strings.info(externalizedStrings.multipleRuntimes);
                  //multiple runtimes found so we will prompt user
                  inquirer.prompt([{
                      name: 'runtimeName',
                      message: externalizedStrings.selectDefaultRuntime,
                      type: 'list',
                      choices: runtimes,
                      when: function () {
                        return noPrompt !== true;
                      }
                    }], function (answers) {
                      if (answers.runtimeName !== undefined) {
                        serverRuntimeContext.runtime = answers.runtimeName;
                        deferred.resolve(serverRuntimeContext);
                      }
                      else {
                        //use the runtime from config.xml
                        cordovaSupport.detect().then(function(configXMLPath) {
                          var mfpConfig = new MFPConfig(configXMLPath);
                          serverRuntimeContext.runtime = mfpConfig.getMFPServerRuntime();
                          deferred.resolve(serverRuntimeContext);
                        }).catch(function () {
                          serverRuntimeContext.runtime = runtimes[0];
                          deferred.resolve(serverRuntimeContext);
                        });
                      }
                    }
                  );
                }
                else {
                  //only one runtime found so we will use this one
                  runtime = runtimes[0];
                  log.silly(moduleName, 'found the following runtime: %j', runtime);
                  serverRuntimeContext.runtime = runtime;
                  deferred.resolve(serverRuntimeContext);
                }
              }
            else {
                log.silly(moduleName, 'validating runtime: %j', runtime);
                //we were given a runtime.  validate that this is still on the server
                if (runtimes.indexOf(runtime) === -1) {
                  strings.warn(strings.format(externalizedStrings.invalidRuntime, runtime));
                  var choices = runtimes.slice();
                  choices.push(externalizedStrings.cancelAction);
                  inquirer.prompt([{
                    name: 'runtimeName',
                    message: externalizedStrings.selectAvailableRuntime,
                    type: 'list',
                    choices: choices
                  }], function (answers) {
                    //check if we should cancel out of the action
                    if (runtimes.indexOf(answers.runtimeName) === -1) {
                      deferred.reject();
                    }
                    else {
                      runtime = answers.runtimeName;
                      serverRuntimeContext.runtime = runtime;
                      deferred.resolve(serverRuntimeContext);
                    }
                  });
                }
                else {
                  //update mfpProjContext with details
                  serverRuntimeContext.runtime = runtime;
                  deferred.resolve(serverRuntimeContext);
                }
              }
            }
            else {
              var noRuntimes = errors.getError('NORUNTIMESEXIST');
              deferred.reject(noRuntimes);
            }
          }).fail(function (error) {
            deferred.reject(error);
          });
        });
      });
    }).fail(function (error) {
        deferred.reject(error);
      }
    );
  return deferred.promise;
};


function getRuntimes(serverName, options) {
  var deferred = Q.defer();

  log.silly(moduleName, 'retrieving runtimes for server: %j', serverName);
  require('../lib/rest-client-api/runtime.js').getRuntimes(options)
    .then(function (results) {
      var runtimes = [];
      for (var i = 0; i < results.projects.length; ++i) {
        runtimes.push(results.projects[i].name);
      }
      deferred.resolve(runtimes);
    })
    .fail(function (err) {
      var url = options.protocol + '://' + options.host + ':' + options.port;
      var runtimeFail = errors.getError('SVRUNREACHABLE', serverName, url);
      err.errno = runtimeFail.errno;
      err.description = runtimeFail.description + '\n' + 'Reason: ' + err.message;
      deferred.reject(err);
  });
  return deferred.promise;
}
