/*
	Licensed Material - Property of IBM

	(C) Copyright 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 keysManager = require('./keys_manager');
var inquirer = require('inquirer');
var errors = require('mdo-errors');
var externalizedStrings = require('../externalizedStrings');
var strings = require('ibm-strings');
var profileIO = require('mdo-server-support').profileIO;
var url = require('url');
var consts = require('./consts');
var log = require('npmlog');
log.level = global.MDO_CLI_LOG_LEVEL || 'info';

function logVerbose(message) { log.verbose(consts.MODULE, message); }

function configList(platformInfo) {
  var d = Q.defer();
  getConfigSettings(platformInfo).then(function(configSettings) {
    for (var key in configSettings) {
      strings.info(key + ': ' + keysManager.getDescriptionForKey(key));
    }
    d.resolve(configSettings);
  }).catch(d.reject);
  return d.promise;
}

function interactiveSetKey(key, platformInfo, configSettings) {
  var d = Q.defer();
  var value;
  logVerbose('Got selected key ' + key);

  keysManager.getPromptForKey(key, configSettings).then(function(keyPrompt) {
    // Got prompt, now prompt the user to set a new value
    var def = Q.defer();
    inquirer.prompt(keyPrompt, def.resolve);
    return def.promise;
  }).then(function(answers) {
    // Got the new value, now write it
    var def;
    var platform = getPlatform(platformInfo);
    var propertiesPath = platformInfo.isCordova ? platform.configXMLPath :
                                                  platform.propertiesPath;

    logVerbose('Got answers ' + answers);

    if (key === 'server' && answers[key] === consts.ENTER_SERVER_URL) {
      // Prompt the user again to enter the url of the new server, then write
      def = Q.defer();
      inquirer.prompt({
        type: 'input',
        name: key,
        message: externalizedStrings.specifyServerURL,
        validate: function(input) {
          return isValidURL(input) ? true : externalizedStrings.enterValidURL;
        }
      }, function(answers) {
        value = answers[key];
        platform.setAppConfigKey(propertiesPath, key, value).then(def.resolve,
                                                                  def.reject);
      });
      return def.promise;
    } else if (key === 'server' && answers[key] !== consts.ENTER_SERVER_URL) {
      // Get the server profile url, then write
      def = Q.defer();
      value = answers[key];
      keysManager.getServerURL(value).then(function(serverUrl) {
        platform.setAppConfigKey(propertiesPath, key, serverUrl)
        .then(def.resolve, def.reject);
      });
      return def.promise;
    } else {
      value = answers[key];
      return platform.setAppConfigKey(propertiesPath, key, value);
    }
  }).then(function() {
    // Successfully wrote new value
    strings.success(strings.format(externalizedStrings.mfpdevAppConfig, key,
                                   value));
    if (platformInfo.isCordova)
      strings.warn(externalizedStrings.runCordovaPrepare);
    // Loop
    configWithNoArgs(platformInfo).then(d.resolve, d.reject);
  }).catch(d.reject);
  return d.promise;
}

function configWithNoArgs(platformInfo) {
  var d = Q.defer();
  var configSettings;

  logVerbose('configWithNoArgs');

  getConfigSettings(platformInfo).then(function(settings) {
    // Got config settings, now prompt the user to select a key
    configSettings = settings;

    logVerbose('Got config settings');

    var def = Q.defer();
    var choices = [new inquirer.Separator(consts.SEPARATOR),
                   externalizedStrings.exitAppConfig,
                   new inquirer.Separator(consts.SEPARATOR)];
    choices = choices.concat(getKeyChoices(configSettings));

    inquirer.prompt({
      type: 'list',
      name: 'key',
      message: externalizedStrings.selectKey,
      choices: choices,
    }, def.resolve);
    return def.promise;
  }).then(function(answers) {
    if (answers.key === externalizedStrings.exitAppConfig) {
      // User chose Exit Config, now exit
      d.resolve();
    } else {
      // User chose a key, continue interactive mode
      interactiveSetKey(answers.key, platformInfo, configSettings)
      .then(d.resolve, d.reject);
    }
  }).catch(d.reject);
  return d.promise;
}

function configWithKey(platformInfo, key) {
  var d = Q.defer();

  logVerbose('configWithKey');

  getConfigSettings(platformInfo).then(function(configSettings) {
    logVerbose('Got configSettings');
    if (!(key in configSettings)) {
      // Invalid key
      d.reject(errors.getError('INVALID_APP_CONFIG_KEY', key));
    } else if (!configSettings[key]) {
      // Key is valid, but not yet set
      strings.warn(strings.format(externalizedStrings.keyIsNotSet, key));
      d.resolve(configSettings[key]);
    } else {
      strings.info(configSettings[key]);
      d.resolve(configSettings[key]);
    }
  }).catch(d.reject);
  return d.promise;
}

function configWithKeyAndVal(platformInfo, key, value) {
  var d = Q.defer();
  var platform = getPlatform(platformInfo);
  var propertiesPath = platformInfo.isCordova ? platform.configXMLPath :
                                                platform.propertiesPath;
  var servers;
  var valueAsServerUrl;

  logVerbose('configWithKeyAndVal');

  getConfigSettings(platformInfo).then(function(configSettings) {
    // Got config settings, now get validate function for key
    var def = Q.defer();
    logVerbose('Got configSettings');

    if (!(key in configSettings)) {
      // Invalid key
      def.reject(errors.getError('INVALID_APP_CONFIG_KEY', key));
    } else if (key === 'server') {
      // The key is valid and it is 'server'
      profileIO.getServerProfiles().then(function(profileInfo) {
        logVerbose(profileInfo);
        servers = profileInfo.servers;
        def.resolve(function(value) {
          // The value is valid only if it is a server name or a URL
          return value in servers || isValidURL(value);
        });
      }).fail(def.reject);
    } else {
      // The key is valid and it is not 'server'
      def.resolve(keysManager.getValidateForKey(key));
    }
    return def.promise;
  }).then(function(validate) {
    // Got the validation function, now validate the value and write it
    var def;

    logVerbose('Got validate function');

    // validate can return an error message, so we check !== true
    if (validate(value) !== true) {
      // Invalid value
      def = Q.defer();
      def.reject(errors.getError('INVALID_APP_CONFIG_VALUE', value, key));
      return def.promise;
    } else if (key === 'server' && value in servers) {
      // Valid value, and a server name was specified
      def = Q.defer();
      keysManager.getServerURL(value).then(function(serverUrl) {
        valueAsServerUrl = serverUrl;
        platform.setAppConfigKey(propertiesPath, key, serverUrl)
        .then(def.resolve, def.reject);
      });
      return def.promise;
    } else {
      return platform.setAppConfigKey(propertiesPath, key, value);
    }
  }).then(function() {
    // Successfully wrote new value
    if (key === 'server' && valueAsServerUrl) {
      strings.success(strings.format(externalizedStrings.setKeyToValueWithURL,
        key, value, valueAsServerUrl));
    } else {
      strings.success(strings.format(externalizedStrings.setKeyToValue, key,
        value));
    }
    if (platformInfo.isCordova)
      strings.warn(externalizedStrings.runCordovaPrepare);
    d.resolve();
  }).catch(d.reject);
  return d.promise;
}

function getKeyChoices(configSettings) {
  return Object.keys(configSettings).map(function(key) {
    return {
      name: keysManager.getHumanReadableForKey(key),
      value: key
    };
  });
}

function isValidURL(value) {
  var parsedURL = url.parse(value);
  return !!(parsedURL.protocol && parsedURL.hostname && parsedURL.port);
}

function getPlatform(platformInfo) {
  return platformInfo.platforms[0];
}

function getConfigSettings(platformInfo) {
  var d = Q.defer();
  var cordovaPlatformIds;
  var platform = getPlatform(platformInfo);
  var propertiesPath = platformInfo.isCordova ? platform.configXMLPath :
                                                platform.propertiesPath;

  if (!propertiesPath) {
    d.reject(errors.getError('NO_CLIENT_PROPERTIES'));
  } else {
    if (platformInfo.isCordova) {
      // If in a cordova project, pass the platform ids to getAppConfigKeys
      cordovaPlatformIds = platformInfo.platforms.filter(function(platform) {
        return platform.platformId.lastIndexOf('cordova', 0) === 0;
      }).map(function(platform) {
        return platform.platformId;
      });
    }
    platform.getAppConfigKeys(propertiesPath, cordovaPlatformIds)
    .then(d.resolve, d.reject);
  }

  return d.promise;
}

module.exports = {
  configList: configList,
  configWithNoArgs: configWithNoArgs,
  configWithKey: configWithKey,
  configWithKeyAndVal: configWithKeyAndVal
};

