/*
	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 serverDefaults = serverSupport.getServerDefaults;
var urlParser = require('url-parse-lax');
var urlFormatter = require('url');

var DEV_SERVER_NAME = 'local';
var urlRegEx = new RegExp(/^(https?)\:\/\/([^:]*)\:([0-9]+)\/?$/);
//URL Regex for LitePlan
var urlRegExLite = new RegExp(/^(https?)\:\/\/(runtime.*)\/([0-9]+)/);
var serverRegEx = new RegExp(/^[a-zA-Z_\+]([a-zA-Z0-9_\-\+\.])*$/);

//Helps us in differentiating between LitePlan and normal plan
var isLite = false;

module.exports = {
    serverRegEx : serverRegEx,
    urlRegEx : isLite ? urlRegExLite : urlRegEx //Changing URL Regex if it is a LitePlan
};

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

  // Handle arguments with nopt
  var opts = nopt({
    url: String,
    login: String,
    password: String,
    contextroot: String,
    timeout: String,
    setdefault: Boolean
  }, {
    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 (opts.url === undefined) {
        var missingURL = errors.getError('ERRMISSINGSERVERURL');
        deferred.reject(missingURL);
      }
      else if (!serverName.match(serverRegEx)) {
        var invalidServerName = errors.getError('INVALIDSERVERNAME');
        deferred.reject(invalidServerName);
      }
      else if (serverName === DEV_SERVER_NAME) {
        var INVALIDSERVERPROFILENAME = errors.getError('INVALIDSERVERPROFILENAME');
        deferred.reject(INVALIDSERVERPROFILENAME);
      }
      else if (opts.url !== undefined && !validateURL(opts.url)) {
        var invalidURL = errors.getError('BADSERVERURL', opts.url, 'add');
        deferred.reject(invalidURL);
      }
      else if (opts.timeout !== undefined && isNaN(opts.timeout)) {
        var invalidTimeout = errors.getError('INVALIDTIMEOUT');
        deferred.reject(invalidTimeout);
      }
      else {
        deferred.resolve(opts);
      }
    }
    else { // invalid arguments found
      var badArguments = errors.getError('BADSERVERARGS', 'add');
      deferred.reject(badArguments);
    }
  }
  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
    addServerInteractive(profiles).then(function () {
      deferred.resolve();
    }).fail(function (error) {
        deferred.reject(error);
    });
  }
  else { // Direct mode
    var serverName = args.argv.remain.shift();
    var error = checkProfileExists(serverName, profiles);
    if (error) {
      deferred.reject(error);
    }
    else {
      var url = args.url;

      // Split url
      //var matchesArray = url.match(urlRegEx);
      var matchedUrl = parseURL(url);
      var protocol = matchedUrl.protocol;
      var host = matchedUrl.hostname;
      var port = matchedUrl.port;
      //This path contains instanceID for LitePlan else "/"
      var path = matchedUrl.path;

      // Login and password entered?
      var login = args.login || serverDefaults.CREDENTIALS;
      var password = args.password || '';
      var contextRoot = args.contextroot || serverDefaults.CONTEXT_ROOT;
      var timeout = args.timeout || serverDefaults.TIMEOUT_SECONDS;
      var setDefault = args.setdefault || false;

      var options = {
        protocol: protocol,
        host: host,
        port: port,
        path: path,
        userName: login,
        userPassword: password,
        contextRoot: contextRoot,
        timeout: timeout
      };

      if(options.path.length > 1){
        isLite = true;
      }

      validateServer(serverName, options, false).then(function (results) {
        profiles.servers[serverName] = {
          'protocol': results.protocol,
          'host': results.host,
          'port': results.port,
          'path': results.path,
          'login': results.userName,
          'password': new Buffer(results.userPassword).toString('base64'),
          'contextRoot': results.contextRoot,
          'timeout': results.timeout
        };

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

        profileIO.writeServerProfiles(profiles).then(function () {
          strings.success(strings.format(externalizedStrings.successServerAdded, serverName));
          deferred.resolve();
        }).fail(function (error) {
          deferred.reject(error);
        });
      }).fail(function (error) {
          deferred.reject(error);
        }
      );
    }
  }
  return deferred.promise;
};

// return the help object for the main mdo help command to show
module.exports.getMainHelp = function() {
  var help = {
    "SERVERS" : [
      {"server add" : externalizedStrings.serverAddDescription}
    ]
  };
  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', 'add.txt');
};

function addServerInteractive(profiles) {
  var deferred = Q.defer();
  inquirer.prompt(
    [
      {
        name: 'serverName',
        message: externalizedStrings.whatServerName,
        type: 'input',
        validate: function (input) {
          if (input === DEV_SERVER_NAME) {
            var INVALIDSERVERPROFILENAME = errors.getError('INVALIDSERVERPROFILENAME');
            strings.fatal('\n' + INVALIDSERVERPROFILENAME.description);
            return false;
          }
          else {
            var error = checkServerName(input, profiles);
            if (error) {
              strings.fatal('\n' + error.description);
              return false;
            }
            else {
              return true;
            }
          }
        }
      },
      {
        name: 'url',
        message: externalizedStrings.whatServerURL,
        type: 'input',
        validate: function (input) {
          if (!validateURL(input)) {
            var badurl = errors.getError('BADSERVERURL', input, 'add');
            strings.fatal('\n' + badurl.description);
            return false;
          }
          else {
            return true;
          }
        }
      },
      {
        name: 'login',
        message: externalizedStrings.whatServerLogin,
        type: 'input',
        default: serverDefaults.CREDENTIALS
      },
      {
        name: 'password',
        message: externalizedStrings.whatServerPassword,
        type: 'password'
      },
      {
        name: 'storePassword',
        message: externalizedStrings.serverCreateConfirmStorePassword,
        type: 'confirm',
        default: true,
        when: function (input) {
          // Only show if customer specified an actual password
          return input.password &&
            input.password.trim().length > 0;
        }
      },
      {
        name: 'contextRoot',
        message: externalizedStrings.whatContextRoot,
        type: 'input',
        default: serverDefaults.CONTEXT_ROOT
      },
      {
        name: 'timeout',
        message: externalizedStrings.whatServerTimeout,
        type: 'input',
        default: serverDefaults.TIMEOUT_SECONDS,
        validate: function (input) {
          if (isNaN(input)) {
            strings.fatal('\n' + errors.getError('INVALIDTIMEOUT').description);
            return false;
          }
          else {
            return true;
          }
        }
      },
      {
        name: 'makeDefault',
        message: externalizedStrings.serverAddMakeDefault,
        type: 'confirm',
        default: true,
        when: function () {
          //Only show if we already have a default
          return profiles.defaultServer !== undefined;
        }
      }
    ],
    function (answers) {
      var serverName = answers.serverName;
      var url = answers.url;

      // Split url
      var matchedUrl = parseURL(url);
      var protocol = matchedUrl.protocol;
      var host = matchedUrl.hostname;
      var port = matchedUrl.port;
      var path = matchedUrl.path;
      var login = answers.login.trim().length > 0 ? answers.login.trim() : serverDefaults.CREDENTIALS;
      var password = answers.password || '';
      var storePassword = answers.storePassword;
      var contextRoot = answers.contextRoot;
      var timeout = answers.timeout;
      var makeDefault = answers.makeDefault !== undefined ? answers.makeDefault : true;

      var options = {
        protocol: protocol,
        host: host,
        port: port,
        path: path,
        userName: login,
        userPassword: password,
        contextRoot: contextRoot,
        timeout: timeout
      };

      if(options.path.length > 1){
        isLite = true;
      }

      validateServer(serverName, options, true)
        .then(function (results) {
          profiles.servers[serverName] = {
            'protocol': results.protocol,
            'host': results.host,
            'port': results.port,
            'path': results.path,
            'login': results.userName,
            'password': results.userPassword !== '' && storePassword ?
              new Buffer(results.userPassword).toString('base64') : '',
            'contextRoot': results.contextRoot,
            'timeout': results.timeout
          };

          if (makeDefault || profiles.defaultServer === undefined) {
            profiles.defaultServer = serverName;
          }
          profileIO.writeServerProfiles(profiles).then(function () {
            strings.success(strings.format(externalizedStrings.successServerAdded, serverName));
            deferred.resolve();
          }).fail(function (error) {
            deferred.reject(error);
          });
        }).fail(function (error) {
          deferred.reject(error);
        }
      );
    }
  );
  return deferred.promise;
}

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

  console.log(externalizedStrings.serverCreateContactingServer);

  serverSupport.getRuntimes(options)
    .then(function (results) {
      var runtimes = [];
      for (var i = 0; i < results.projects.length; ++i) {
        runtimes.push(results.projects[i].name);
      }
      if (runtimes.length === 0) {
        strings.info(externalizedStrings.serverRuntimeMessage);
      }
      else {
        console.log(externalizedStrings.serverRuntimeList [strings.theme.info], runtimes.toString());
      }
      deferred.resolve(options);
    })
    .fail(function (err) {
        if (err.statusCode === 301){
          deferred.reject("http protocol is not supported. Please use https to register the server");
        }
        // Ask for new credentials when the response is "forbidden" (401)
        if (err.statusCode === 401) {
          if (interactiveMode) {
            strings.error(externalizedStrings.serverCreateWrongCredentials);
            strings.error(err.message);
            inquirer.prompt(
              [{
                name: 'enterCredentials',
                message: externalizedStrings.enterCredentialsAgain,
                type: 'confirm',
                default: true,
              },
                {
                  name: 'login',
                  message: externalizedStrings.whatServerLogin,
                  type: 'input',
                  when: function (answers) {
                    return answers.enterCredentials;
                  }
                },
                {
                  name: 'password',
                  message: externalizedStrings.whatServerPassword,
                  type: 'password',
                  when: function (answers) {
                    return answers.enterCredentials;
                  }
                }],
              function (answers) {
                if (answers.enterCredentials === true) {
                  options.userName = answers.login.trim().length > 0 ? answers.login.trim() : serverDefaults.CREDENTIALS;
                  options.userPassword = answers.password || '';
                  validateServer(serverName, options, true)
                    .then(function (results) {
                      deferred.resolve(results);
                    }).fail(function (error) {
                      deferred.reject(error);
                    }
                  );
                }
                else {
                  var commandFailed = errors.getError('MDOSERVERCOMMANDFAILED', 'add');
                  deferred.reject(commandFailed);
                }
              }
            );
          }
          else {
            //unauthorized error
            strings.error(externalizedStrings.serverCreateWrongCredentials);
            strings.error(err.message);
            var commandFailed = errors.getError('MDOSERVERCOMMANDFAILED', 'add');
            deferred.reject(commandFailed);
          }
        } else {
          // Display server unreachable error
          var serverUnreachable;
          if(isLite){
            serverUnreachable = errors.getError('SVRUNREACHABLE', serverName,
            urlFormatter.format({protocol:(options.protocol || 'http') , hostname: options.host, port: options.port, pathname: options.path}));
          }
          else{
            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(options);
              }
              else {
                var commandFailed = errors.getError('MDOSERVERCOMMANDFAILED', 'add');
                deferred.reject(commandFailed);
              }
            }
          );
        }
      }
    );
  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);
  if(parsedURL.path.length > 1 && parsedURL.path[parsedURL.path.length - 1] === "/" )
    parsedURL.path = parsedURL.path.slice(0,-1);
  return parsedURL;
}

function checkServerName(name, profiles) {
  if (!name.match(/^[a-zA-Z_\+]([a-zA-Z0-9_\-\+\.])*$/)) {
    return errors.getError('INVALIDSERVERNAME');
  }
  return checkProfileExists(name, profiles);
}

function checkProfileExists(name, profiles) {
  if (profiles.servers[name] !== undefined) {
    return errors.getError('SVREXISTS', name);
  }
}
