/*
	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 inquirer = require('inquirer');
var log = require('npmlog');
var nopt = require('nopt');
var path = require('path');
var Q = require('q');
var fs = require('fs');
var errors = require('mdo-errors');
var strings = require('ibm-strings');
var externalizedStrings = require('./externalizedStrings');
var consts = require('./constants');

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

/**
 * actually do the work of the command
 */
module.exports.run = function(argv) {
  var deferred = Q.defer();
  var appTypes = {};
  var appTypeExtensions = getAppTypeExtensions();  

  if (appTypeExtensions) {
    appTypeExtensions.forEach(function(appType){
      var appTypeName = appType.name;
      appTypes[appTypeName] =
        {
          "run": appType.export.run,
        };
    });
    log.verbose(consts.LOG_PREFIX, 'Supported app types ' + JSON.stringify(appTypes));
  }

  var opts = nopt({
    appType: [ consts.IONIC, consts.NATIVE ],
    appId: String,
    platforms: [ consts.ANDROID, consts.IOS ] ,
    services: [],
    appVersion: String,  
    appConfig: String, 
    serverConfig: String,      
    debug: Boolean,
    ddebug: Boolean,
  }, {
    t: '--appType',
    i: '--appId',
    av: '--appVersion',    
    p: '--platforms',
    s: '--services',
    ac: '--appConfig',
    sc: '--serverConfig',    
    d: '--debug',
    dd: '--ddebug'   
  }, argv, 0);

  if (opts.argv.original.length === 0) { // Interactive mode
    interactive().then(function (answers) {
      if (!Array.isArray(answers.platforms)) {
        answers.platforms = [answers.platforms];
      }
      var appTypeExtension = appTypes[answers.appType];
      var appDetails = {
        appName: answers.appName,
        appId: answers.appId,
        appVersion: answers.appVersion,
        platforms: answers.platforms,
        services: answers.services,
      };
      return executeCommand(appTypeExtension, appDetails);       
    }).catch(function (error) {
      if (error.description) {
        strings.fatal(error.description);
      } else {
        strings.fatal(strings.format(externalizedStrings.cmdFailed, error));
      } 
      deferred.reject(error);       
    });    
  }
  else { // Direct mode
    var params = argv;

    if (!isEmpty(opts.appConfig)) {
      if (isInvalidFile(opts.appConfig) || (!isEmpty(opts.serverConfig) && isInvalidFile(opts.serverConfig))) {
        var missingConfig = errors.getError('ERRINVALIDFILE');
        strings.fatal(missingConfig.description);
        deferred.reject(missingConfig);
      } else {
        params = parseAppConfig(opts.appConfig);
        log.verbose(consts.LOG_PREFIX, 'App config json output : ' + params);  
        var configDetails = {
          appConfig: opts.appConfig,
          serverConfig: opts.serverConfig
        }        
        directMode(params, appTypes, configDetails).then(function() {           
          deferred.resolve();
        }).catch(function (error) {
          deferred.reject(error);
        }); 
      }    
    } else {
      directMode(params, appTypes).then(function() {           
        deferred.resolve();
      }).catch(function (error) {
        deferred.reject(error);
      }); 
    }
  }
  return deferred.promise;
};

function directMode(params, appTypes, configDetails) {
  var deferred = Q.defer();

  validate(params).then(function(options) {   
    var appTypeExtension = appTypes[options.appType];
    var appDetails = {
      appName: options.argv.remain[0],
      appId: options.appId,
      appVersion: options.appVersion,
      platforms: options.platforms.split(','),
      services: options.services ? options.services.split(',') : []
    };   
    return executeCommand(appTypeExtension, appDetails, configDetails);
  }).then(function() {           
    deferred.resolve();
  }).catch(function (error) {
    if (error.description) {
      strings.fatal(error.description);
    } else {
      strings.fatal(strings.format(externalizedStrings.cmdFailed, error));
    }
    deferred.reject(error);
  }); 

  return deferred.promise;
}

/**
 * print command specific help to the console
 */
module.exports.help = function() {
  return path.resolve(__dirname, '..', 'doc', 'create.txt');
};

function executeCommand(appTypeExtension, appDetails, configDetails) {
  var deferred = Q.defer();  

  var projectFolder = path.join(process.cwd(), appDetails.appName);

  strings.info(externalizedStrings.creatingApplication);

  if (!fs.existsSync(projectFolder)) {
    fs.mkdirSync(projectFolder);  
  }   

  var configFolder = path.join(projectFolder, consts.MFPCONFIG);
  if (!fs.existsSync(configFolder)) {
    fs.mkdirSync(configFolder);  
  } 

  appTypeExtension.run(appDetails, configDetails).then(function(appInfos) {
    strings.success(strings.format(externalizedStrings.successAppCreated, appDetails.appName));
    log.verbose(consts.LOG_PREFIX, 'Writing the configuration...');
    writeConfiguration(configFolder, appInfos, configDetails);
    deferred.resolve();
  }).catch(function (error) {   
    deferred.reject(error);
  });

  return deferred.promise;
}

function getAppTypeExtensions() {
  var _appTypeExtensions;

  if (!_appTypeExtensions) {
    var findExtensions = require('pluggington');

    _appTypeExtensions = findExtensions(global.MDO_MAIN_EXTENSIONS_PATH, 'mdo.cli.app.create');
  }

  return _appTypeExtensions;
}

function parseAppConfig(config) {
  var argv = [];
  var platforms = [];
  var services = [];
  if (config) {
    const appConfig = JSON.parse(fs.readFileSync(config));
    argv.push("-t");
    argv.push(appConfig.type);
    if (appConfig.platforms) {
      appConfig.platforms.forEach(function(platform, index) {
        if (index == 0) {
          argv.push(platform.appName);
          argv.push("-i");
          argv.push(platform.applicationKey.clientPlatform == 'android' ? platform.applicationKey.packageName : platform.applicationKey.bundleId);
          argv.push("-av");
          argv.push(platform.applicationKey.version); 
          platforms.push(platform.applicationKey.clientPlatform);                 
        } else {
          platforms.push(platform.applicationKey.clientPlatform);
        }
      });
      argv.push("-p");
      argv.push(platforms.join());
    }
    if (appConfig.services) {
      appConfig.services.forEach(function(service) {
        services.push(service.name);
      });
      argv.push("-s");
      argv.push(services.join());
    }         
  }
  return argv;
}

function writeConfiguration(configFolder, appInfos, configDetails) { 
  var serverDetails = null;
  var appDetails = null;

  const appJsonPath = path.join(configFolder, 'app_config.json');

  if (configDetails) {
    if (configDetails.appConfig) {
      var appData = fs.readFileSync(configDetails.appConfig);
      appData = appData.toString().replace(/appName/g, 'displayName');
      appDetails = JSON.parse(appData);
      delete appDetails.type;    
      fs.writeFileSync(appJsonPath, JSON.stringify(appDetails, null, 2));
    }
    if (configDetails.serverConfig) {
      serverDetails = fs.readFileSync(configDetails.serverConfig);
      const serverJsonPath = path.join(configFolder, 'server_config.json');
      fs.writeFileSync(serverJsonPath, serverDetails);
    }      
  } else {
    appDetails = {
      platforms: [],
    };
    appDetails.platforms = appInfos;
    fs.writeFileSync(appJsonPath, JSON.stringify(appDetails, null, 2));
  }
}

function interactive() {
  var deferred = Q.defer();
  inquirer.prompt(
    [
      {
        name: 'appName',
        message: externalizedStrings.enterName,
        type: 'input',
        validate: function (input) {
          if (input > 64 || !/^([a-zA-Z0-9])+$/g.test(input)) {
            var invalidAppName = errors.getError('INVALIDAPPNAME');
            strings.fatal('\n' +  invalidAppName.description);
            return false;
          }           
          return true;
        }        
      },
      {
        name: 'appType',
        message: externalizedStrings.enterType,
        type: 'list',
        choices: [{ 'name': consts.IONIC, 'value': consts.IONIC },
                  { 'name': consts.NATIVE, 'value': consts.NATIVE },
                ],
      },  
      {
        name: 'appId',
        message: externalizedStrings.enterAppId,
        type: 'input',
        //default: `com.${answers.appName}`,
        validate: function (input) {
          if (isInvalidAppId(input)) {
            var invalidAppId = errors.getError('INVALIDAPPID');
            strings.fatal('\n' + invalidAppId.description);
            return false;
          }       
          return true;
        }        
      },
      {
        name: 'appVersion',
        message: externalizedStrings.enterVersion,
        type: 'input',
        default: '1.0.0',
        validate: function (input) {
          if (isInvalidVersion(input)) {
            var invalidVersion = errors.getError('INVALIDVERSION');
            strings.fatal('\n' +  invalidVersion.description);
            return false;
          }       
          return true;
        } 
      },
      {
        name: 'platforms',
        message: externalizedStrings.enterPlatforms,
        type: 'checkbox',
        choices: [{ 'name': consts.ANDROID, 'value': consts.ANDROID },
                  { 'name': consts.IOS, 'value': consts.IOS },
                ],
        when: function(answers) {
          return answers.appType == consts.IONIC;
        },                
        validate: function (input) {
          if (input.length <= 0) {
            var invalidPlatform = errors.getError('ERRINVALIDPLATFORM');
            strings.fatal('\n' +  invalidPlatform.description);
            return false;
          }       
          return true;
        }                
      },
      {
        name: 'platforms',
        message: externalizedStrings.enterPlatforms,
        type: 'list',
        choices: [{ 'name': consts.ANDROID, 'value': consts.ANDROID },
                  { 'name': consts.IOS, 'value': consts.IOS },
                ],
        when: function(answers) {
          return answers.appType == consts.NATIVE;
        },                
        validate: function (input) {
          if (input.length <= 0) {
            var invalidPlatform = errors.getError('ERRINVALIDPLATFORM');
            strings.fatal('\n' +  invalidPlatform.description);
            return false;
          }       
          return true;
        }                
      },      
      {
        name: 'services',
        message: externalizedStrings.enterServices,
        type: 'checkbox',
        choices: [{ 'name': consts.PUSH, 'value': consts.PUSH },
                  { 'name': consts.ANALYTICS, 'value': consts.ANALYTICS },
                  { 'name': consts.LIVEUPDATE, 'value': consts.LIVEUPDATE },
                ],              
      },            
    ],
    function (answers) {
      deferred.resolve(answers);
    }
  );

  return deferred.promise;
}

function validate(argv) {
  var deferred = Q.defer();

  var opts = nopt({
    appType: [ consts.IONIC, consts.NATIVE ],
    appId: String,
    platforms: String ,
    appVersion: String,  
    services: String,
    appConfig: String, 
    serverConfig: String, 
  }, {
    t: '--appType',
    i: '--appId',
    av: '--appVersion',    
    p: '--platforms', 
    s: '--services',
    ac: '--appConfig',
    sc: '--serverConfig',
  }, argv, 0);

  if (opts.argv.remain.length > 0) {
    if (opts.argv.remain.length === 1) {
      var appName = opts.argv.remain[0];
      if (appName > 64 || !/^([a-zA-Z0-9])+$/g.test(appName)) {
        var invalidAppName = errors.getError('INVALIDAPPNAME');
        deferred.reject(invalidAppName);
      } else if (isEmpty(opts.appType)) {
        var missingAppType = errors.getError('ERRMISSINGAPPTYPE');
        deferred.reject(missingAppType);      
      } else if (isEmpty(opts.appId)) {
        var missingAppId = errors.getError('ERRMISSINGAPPID');
        deferred.reject(missingAppId);   
      } else if (isInvalidAppId(opts.appId)) {
        var invalidAppId = errors.getError('INVALIDAPPID');
        deferred.reject(invalidAppId);
      } else if (isEmpty(opts.platforms)) {
        var missingPlatform = errors.getError('ERRMISSINGPLATFORM');
        deferred.reject(missingPlatform);
      } else if (isInvalidPlatform(opts.platforms, opts.appType)) {
        var missingPlatform = errors.getError('ERRINVALIDPLATFORM');
        deferred.reject(missingPlatform);
      } else if (isInvalidService(opts.services)) {
        var missingService = errors.getError('ERRINVALIDSERVICE');
        deferred.reject(missingService);
      } else if (isEmpty(opts.appVersion)) {
        var missingVersion = errors.getError('ERRMISSINGVERSION');
        deferred.reject(missingVersion);
      } else if (isInvalidVersion(opts.appVersion)) {
        var invalidVersion = errors.getError('INVALIDVERSION');
        deferred.reject(invalidVersion);
      } else {
        deferred.resolve(opts);
      }
    } else { // invalid arguments found
      var badArguments = errors.getError('BADAPPCREATEARGS');
      deferred.reject(badArguments);
    }
  } else {
    var missingAppName = errors.getError('ERRMISSINGAPPNAME');
    deferred.reject(missingAppName);
  }  

  return deferred.promise;
};

function isEmpty(input) {
  return input === undefined || input.trim().length < 0;
}

function isInvalidAppId(appId) {
  return appId.length > 64 || !/^[a-z]*(\.[a-z]*)+[a-z]$/g.test(appId) ||
        /\.new\b|new\.\b/g.test(appId);
}

function isInvalidVersion(version) {
  return version.length > 12 || !/^\d+(\.\d+)?\.?\d+$/g.test(version);
}

function isInvalidPlatform(platforms, appType) {
  if (!isEmpty(platforms)) {
    const validPlatforms = [consts.ANDROID, consts.IOS];
    const appPlatforms = platforms.split(',');
    if (appType == consts.NATIVE && appPlatforms.length > 1) {
      return true;
    }
    const invalidPlatforms = appPlatforms.filter(function (platform) {
      return validPlatforms.indexOf(platform) <= -1;
    });
    if (invalidPlatforms.length > 0) {
      return true;
    }
    return false;
  } else {
    return true;
  } 
}

function isInvalidService(services) {
  if (!isEmpty(services)) {
    const validServices = [consts.PUSH, consts.ANALYTICS, consts.LIVEUPDATE];
    const appServices = services.split(',');
    const invalidServices = appServices.filter(function (service) {
      return validServices.indexOf(service) <= -1;
    });
    if (invalidServices.length > 0) {
      return true;
    }  
  }
  return false;
}

function isInvalidFile(file) {
  if (!isEmpty(file)) {
    if (!fs.existsSync(file)) {
      return true;
    } 
  }

  return false;
}