/*
	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 nopt = require('nopt');
var path = require('path');

var strings = require('ibm-strings');
var errors = require('mdo-errors');
var serverSupport = require('mdo-server-support');
var profileIO = serverSupport.profileIO;
var externalizedStrings = require('./externalizedStrings');
var urlParser = require('url-parse-lax');
var urlFormatter = require('url');
var serverRegEx = require('./server-add').serverRegEx;

var LOCAL_PROFILE = 'local';

module.exports.validate = function(args) {
  var deferred = Q.defer();

  // Handle arguments with nopt
  var opts = nopt({
    name: String,
    url: String,
    login: String,
    password: String,
    contextroot: String,
    timeout: String,
    setdefault: Boolean
  }, {
    n: '--name',
    u: '--url',
    l: '--login',
    p: '--password',
    c: '--contextroot',
    t: '--timeout',
    s: '--setdefault'
  }, args, 0);

  if (opts.argv.remain.length > 0) {
    if (opts.argv.remain.length === 1) {
      var serverName = opts.argv.remain[0];
      if (!serverName.match(serverRegEx)) {
        deferred.reject(errors.getError('INVALIDSERVERNAME'));
      }
      else if (serverName === LOCAL_PROFILE && opts.name !== undefined) {
        deferred.reject(errors.getError('CANNOTRENAMELOCAL'));
      }
      else if (opts.name !== undefined && opts.name === LOCAL_PROFILE) {
        deferred.reject(errors.getError('INVALIDSERVERPROFILENAME'));
      }
      else if (opts.name !== undefined && !opts.name.match(serverRegEx)) {
        deferred.reject(errors.getError('INVALIDSERVERNAME'));
      }
      else if (opts.url !== undefined && !validateURL(opts.url)) {
        deferred.reject(errors.getError('BADSERVERURL', opts.url, 'edit'));
      }
      else if (opts.timeout !== undefined && isNaN(opts.timeout)) {
        deferred.reject(errors.getError('INVALIDTIMEOUT'));
      }
      else {
        deferred.resolve(opts);
      }
    }
    else { // invalid arguments found
      deferred.reject(errors.getError('BADSERVERARGS', 'edit'));
    }
  }
  else {
    deferred.resolve(opts);
  }
  return deferred.promise;
};


module.exports.run = function (args, profiles) {
  var deferred = Q.defer();

  if (args.argv.remain.length === 0) { // Interactive mode
    editServerInteractive(profiles).then(function () {
      deferred.resolve();
    }, function (error) {
      deferred.reject(error);
    });
  }
  else { // Direct mode
    var serverName = args.argv.remain.shift();

    if (!profiles.servers.hasOwnProperty(serverName)) {
      deferred.reject(errors.getError('SVRDOESNOTEXISTS', serverName));
    }
    else if (args.name !== undefined &&
      profiles.servers[args.name] !== undefined) {
      deferred.reject(errors.getError('SVREXISTS', args.name));
    }
    else {
      var updateDefaultServer = false;
      if (args.name !== undefined) {
        profiles.servers[args.name] = profiles.servers[serverName];
        delete profiles.servers[serverName];
        if (serverName === profiles.defaultServer) {
          updateDefaultServer = true;
        }
        serverName = args.name;
      }
      var server = profiles.servers[serverName];
      var setDefault = args.setdefault || false;

      if (setDefault || updateDefaultServer) {
        profiles.defaultServer = serverName;
      }

      if (args.url !== undefined) {
        var matchedUrl = parseURL(args.url);
        server.protocol = matchedUrl.protocol;
        server.host = matchedUrl.hostname;
        server.port = matchedUrl.port;
      }

      server.login = args.login || server.login;
      server.password = args.password !== undefined ?
        new Buffer(args.password).toString('base64') : server.password;
      server.contextRoot = args.contextroot || server.contextRoot;
      server.timeout = args.timeout || server.timeout;

      var options = {
        protocol: server.protocol,
        host: server.host,
        port: server.port,
        userName: server.login,
        userPassword: (new Buffer(server.password, 'base64')).toString(),
        contextRoot: server.contextRoot,
        timeout: server.timeout
      };

      if (args.url !== undefined || args.login !== undefined ||
          args.password !== undefined || args.timeout !== undefined ||
          args.contextroot !== undefined) {

        validateServer(serverName, options, false)
          .then(function () {
            return profileIO.writeServerProfiles(profiles);
          })
          .then(function () {
            strings.success(strings.format(externalizedStrings.successServerEdited, serverName));
            deferred.resolve();
          })
          .catch(function (error) {
            deferred.reject(error);
          });
      }
      else if (args.name !== undefined || args.setdefault !== undefined) {
        profileIO.writeServerProfiles(profiles)
          .then(function () {
            strings.success(strings.format(externalizedStrings.successServerEdited, serverName));
            deferred.resolve();
          }, function (error) {
            deferred.reject(error);
          });
      }
      else {
        deferred.reject(errors.getError('BADSERVERARGS', 'edit'));
      }
    }
  }
  return deferred.promise;
};

// return the help object for the main mdo help command to show
module.exports.getMainHelp = function() {
  var help = {
    "SERVERS" : [
      {"server edit" : externalizedStrings.serverEditDescription}
    ]
  };
  return help;
};

// return the path to the help file (relative to basePath) for the specific command
module.exports.getHelpFilePath = function(basePath) {
  return path.join(basePath, 'mdo-cmd-server', 'doc', 'edit.txt');
};


function editServerInteractive(profiles) {
  var deferred = Q.defer();

  var choices = [];
  for (var i in profiles.servers) {
    var choicesName = i === profiles.defaultServer ? i + ' (default)' : i;
    choices.push(choicesName);
  }
  if (choices.length === 0) {
    deferred.reject(errors.getError('MISSINGSERVERPROFILES', 'edit'));
  }
  else {
    inquirer.prompt([{
      name: 'serverName',
      message: externalizedStrings.selectServerProfileToEdit,
      type: 'list',
      choices: choices
    }], function (answers) {
      var serverName = answers.serverName;
      if (serverName.indexOf(' (default)') !== -1) {
        //remove ' (default)' from prompt name
        serverName = serverName.slice(0, -10);
      }
      var server = profiles.servers[serverName];
      var hiddenPassword = server.password !== undefined ?
        (new Buffer(server.password, 'base64')).toString().replace(/./g, '*') : '';
      var login = server.login || 'admin';
      var contextroot = server.contextRoot || 'mfpadmin';
      var timeout = server.timeout || '30';
      var url = urlFormatter.format({protocol:(server.protocol || 'http') ,hostname:server.host,port:server.port});
      var serverChoices = [];

      serverChoices.push('url (' + url + ')');
      serverChoices.push('login (' + login + ')');
      serverChoices.push('password (' + hiddenPassword + ')');
      serverChoices.push('context root (' + contextroot + ')');
      serverChoices.push('timeout (' + timeout + ')');

      if (serverName !== profiles.defaultServer) {
        serverChoices.push(externalizedStrings.serverEditMakeDefault);
      }

      var serverInfo = {
        serverName: serverName,
        server: profiles.servers[serverName],
        serverChoices: serverChoices,
        hiddenPassword:  hiddenPassword,
        validateServer: false,
      };

      promptValueToChange(profiles, serverInfo)
        .then(function () {
          deferred.resolve();
        }, function (error) {
          deferred.reject(error);
        });
    });
  }
  return deferred.promise;
}

function promptValueToChange(profiles, serverInfo) {
  var deferred = Q.defer();

  inquirer.prompt([{
      name: 'valueToChange',
      message: externalizedStrings.selectValueToChange,
      type: 'list',
      choices: serverInfo.serverChoices
    }], function (answers) {
      var match = answers.valueToChange.match(/ [(\()][\s\S]*(\))$/);
      if (match !== null) {
        var valueToChange = match[0];
        var key = answers.valueToChange.split(valueToChange)[0];
        promptNewValue(valueToChange, key, profiles, serverInfo)
          .then(function() {
            deferred.resolve();
          }, function (error) {
            deferred.reject(error);
          });
      }
      else {
        //need to set the server as default in server_profiles.json
        profiles.defaultServer = serverInfo.serverName;
        //we don't want to ask to set as default again
        serverInfo.serverChoices.pop();
        promptEditAnother(profiles, serverInfo)
          .then(function() {
            deferred.resolve();
          }, function (error) {
            deferred.reject(error);
          });
      }
    }
  );
  return deferred.promise;
}

function promptNewValue(valueToChange, key, profiles, serverInfo) {
  var deferred = Q.defer();

  inquirer.prompt([{
      name: 'newValue',
      message: strings.format(externalizedStrings.enterValueForKey, key),
      type: 'input',
      default: valueToChange.slice(2, valueToChange.length - 1),
      validate: function (input) {
        if (key === 'url') {
          if (!validateURL(input)) {
            var badurl = errors.getError('BADSERVERURL', input, 'edit');
            strings.fatal('\n' + badurl.description);
            return false;
          }
          else {
            return true;
          }
        }
        else if (key === 'timeout') {
          if (isNaN(input)) {
            strings.fatal('\n' + errors.getError('INVALIDTIMEOUT').description);
            return false;
          }
          else {
            return true;
          }
        }
        else {
          return true;
        }
      }
    }], function (answers) {
      serverInfo.validateServer = true;
      if (key === 'url') {
        var matchedUrl = parseURL(answers.newValue);
        serverInfo.server.protocol = matchedUrl.protocol;
        serverInfo.server.host = matchedUrl.hostname;
        serverInfo.server.port = matchedUrl.port;
        serverInfo.serverChoices[0] = ('url (' + answers.newValue + ')');
      }
      else if (key === 'login') {
        serverInfo.server[key] = answers.newValue;
        serverInfo.serverChoices[1] = ('login (' + answers.newValue + ')');
      }
      else if (key === 'password') {
        var password = answers.newValue.trim();
        if (password !== serverInfo.hiddenPassword) {
          serverInfo.server[key] = password !== '' ? new Buffer(password).toString('base64') : '';
          var hiddenPassword = password.replace(/./g, '*');
          serverInfo.hiddenPassword = hiddenPassword;
          serverInfo.serverChoices[2] = ('password (' + hiddenPassword + ')');
        }
      }
      else if (key === 'context root') {
        serverInfo.server.contextRoot = answers.newValue;
        serverInfo.serverChoices[3] = ('context root (' + answers.newValue + ')');
      }
      else {
        //timeout
        serverInfo.server[key] = answers.newValue || '';
        serverInfo.serverChoices[4] = ('timeout (' + answers.newValue + ')');
      }
      promptEditAnother(profiles, serverInfo)
        .then(function() {
          deferred.resolve(serverInfo);
        }, function (error) {
          deferred.reject(error);
        });
    }
  );
  return deferred.promise;
}

function promptEditAnother (profiles, serverInfo) {
  var deferred = Q.defer();

  inquirer.prompt([{
      name: 'editAnotherSetting',
      message: externalizedStrings.editAnotherSetting,
      type: 'confirm',
      default: false,
    }], function (answers) {
      if (answers.editAnotherSetting === true) {
        promptValueToChange(profiles, serverInfo)
          .then(function () {
            deferred.resolve();
          }, function (error) {
            deferred.reject(error);
          });
      }
      else {
        var password = serverInfo.server.password || '';
        var options = {
          protocol: serverInfo.server.protocol,
          host: serverInfo.server.host,
          port: serverInfo.server.port,
          userName: serverInfo.server.login,
          userPassword: (new Buffer(password, 'base64')).toString(),
          contextRoot: serverInfo.server.contextRoot,
          timeout: serverInfo.server.timeout
        };

        if (serverInfo.validateServer) {
          validateServer(serverInfo.serverName, options, true)
            .then(function () {
              return profileIO.writeServerProfiles(profiles);
            })
            .then(function () {
              strings.success(strings.format(externalizedStrings.successServerEdited, serverInfo.serverName));
              deferred.resolve();
            })
            .catch(function (error) {
              deferred.reject(error);
            });
        }
        else {
          profileIO.writeServerProfiles(profiles)
            .then(function () {
              strings.success(strings.format(externalizedStrings.successServerEdited, serverInfo.serverName));
              deferred.resolve();
            }, function (error) {
              deferred.reject(error);
            });
        }
      }
    }
  );

  return deferred.promise;
}

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

  console.log(externalizedStrings.serverCreateContactingServer);

  inquirer.prompt(
    [{
      name: 'password',
      message: externalizedStrings.whatServerPassword,
      type: 'password',
      when: function () {
        return (options.userPassword === undefined || options.userPassword === '');
      }
    }],
    function (answers) {
      if (answers.password !== undefined) {
        options.userPassword = answers.password;
      }
      serverSupport.getRuntimes(options)
        .then(function () {
           deferred.resolve();
        }, function (err) {
          if (err.statusCode === 401) {
            //unauthorized error
            strings.error(externalizedStrings.serverCreateWrongCredentials);
            strings.error(err.message);
            deferred.reject(errors.getError('MDOSERVERCOMMANDFAILED', 'edit'));
          }
          else {
             // Display server unreachable error
            var serverUnreachable = errors.getError('SVRUNREACHABLE', serverName,
              urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port}));

            strings.warn(serverUnreachable.description);
            strings.warn(err.message);
            inquirer.prompt(
              [{
                name: 'saveAnyways',
                message: externalizedStrings.serverUnreachableSaveAnyways,
                type: 'confirm',
                default: false,
                when: function () {
                  return (interactiveMode === true);
                }
              }],
              function (answers) {
                if (answers.saveAnyways === undefined || answers.saveAnyways === true) {
                  deferred.resolve();
                }
                else {
                  deferred.reject(errors.getError('MDOSERVERCOMMANDFAILED', 'edit'));
                }
              }
            );
          }
        });
    }
  );
  return deferred.promise;
}

function validateURL(url){
  var serverUrl = urlParser(url);
  if(serverUrl.protocol !== null && serverUrl.host !== null && serverUrl.port !== null) return true;
  else return false;
}

function parseURL(url){
  var parsedURL = urlParser(url);
  //removes the colon from the protocol
  parsedURL.protocol = parsedURL.protocol.slice(0,-1);

  return parsedURL;
}
