// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: apiconnect-cli-apps
// US Government Users Restricted Rights - Use, duplication or disclosure
// restricted by GSA ADP Schedule Contract with IBM Corp.

var Promise = require('bluebird');
var _ = require('lodash');
var cliConfig = require('apiconnect-cli-config');
var debug = require('debug')('apiconnect-cli-apps:lib:deploy');
var f = require('util').format;
var logger = require('apiconnect-cli-logger');
var path = require('path');
var libertyPublish = require('./publish/liberty-publish');
var bluemixPublish = require('./publish/bluemix-publish');
var cliUtil = require('apiconnect-cli-util');

/**
 * @typedef RuntimeInfo
 * @type Object
 * @property {string} hostname Hostname for the application.
 * @property {string} [header] Optional header that must be set to route to the application.
 * @property {string} managementUri URL for managing the application runtime.
 * @property {object} extendedStatus Full output from runtime deployer
 */

/**
 * Deploy project to runtime service.
 *
 * @param {object} [options] options.
 * @param {string} [options.projectDir] Directory to project files. Defaults to process.cwd()
 * @param {string} [options.server] API Management server.
 * @param {string} [options.organization] Provider org name.
 * @param {string} [options.app] Application name. Defaults to project name if available.
 * @param {string} [options.title] Application title. Defaults to application name.
 *
 * @returns {RuntimeInfo}
 */
function deployProject(options) {
  options = options || {};
  options.projectDir = options.projectDir || process.cwd();
  options = options || {};
  debug('project dir: %s', options.projectDir);
  var dirInfo = cliConfig.inspectPath(options.projectDir, { incNodeProjects: true });
  debug('inspect %j', dirInfo);

  if (dirInfo.type === 'project') {
    options.packageName = dirInfo.name;
    options.projectType = dirInfo.projectType;

    var config = cliConfig.loadConfig();
    var app = config.getOne('app') || { data: {} };
    options.server = options.server || app.hostname;
    options.organization = options.organization || app.data.orgs;
    options.app = options.app || app.data.apps || options.packageName;
    options.title = options.title || options.app || app.data.apps || options.packageName;

    return lookupRuntimeProviderOrgAndApp(options).then(function(info) {
      debug('lookup info', info);
      var providerOrg = info[0];
      var providerApp = info[1];

      if (!providerOrg) {
        throw new Error(f('No such provider organization: %s', options.organization));
      }

      if (!providerApp) {
        throw new Error(f('No such provider application: %s', options.app));
      }

      if (providerApp.bluemix) {
        logger.writeln('Deploying to Bluemix');
        return bluemixPublish(
          providerOrg, providerApp, path.resolve(options.projectDir), options.server, options.projectType
        ).then(function(res) {
          _.each(res.messages, function(m) {
            logger.writeln(m);
          });
        });
      } else if (providerApp.liberty) {
        return libertyPublish(
          options.server, providerOrg, providerApp, path.resolve(options.projectDir), options.projectType
        ).then(function(res) {
          logger.writeln();
          logger.writeln('Upload to liberty server completed succesfully.');
          logger.writeln('Applications may take a few minutes to update and start.');
          logger.writeln();
          logger.writeln(
            'Collectives admin center: https://%s:%s/adminCenter.',
            res.liberty.controllerHosts[0].host,
            res.liberty.controllerHosts[0].port
          );
          logger.writeln();

          var pkgJson = require(path.resolve(options.projectDir, 'package.json'));
          var isGateway = pkgJson.APIConnectGateway;
          if (!isGateway) {
            logger.writeln('Please update your swagger with:');
            logger.writeln('API target urls: %s', res.ihsInfo.join(', '));
            logger.writeln('host header: %s', res.liberty.vHostHeader);
          }
        });
      }
      throw new Error('Unable to resolve runtime server');
    });
  } else {
    throw new Error(f('Unsupported project type in %s', options.projectDir));
  }
}
exports.deployProject = deployProject;

function lookupRuntimeProviderOrgAndApp(options) {
  var server = options.server;
  var orgName = options.organization;
  var appName = options.app;
  var appTitle = options.title;
  debug(server, orgName, appName);


  if (!server) {
    throw new Error('--server is required or needs to be set via $apic config:set app');
  }

  if (!orgName) {
    throw new Error('--organization is required or needs to be set via $apic config:set app');
  }

  var auth = cliUtil.Netrc.lookup(server);
  var providerOrgPromise = cliUtil.Orgs.listOrgsAsync(auth.login, auth.password, server).then(function(providerOrgs) {
    var providerOrg = null;
    providerOrg = _.filter(providerOrgs, { name: orgName });
    if (providerOrg.length !== 1) {
      throw new Error(f('No such provider organization: %s', orgName));
    }
    providerOrg = providerOrg[0];

    if (providerOrg.externalNativeOrgId) {
      providerOrg.bluemix = {
        inviteUri: providerOrg.externalInviteOverride,
        organization: providerOrg.externalNativeOrgId,
        space: providerOrg.externalSpaceId,
      };
    }
    return providerOrg;
  });

  var providerAppPromise = providerOrgPromise.then(function(providerOrg) {
    return cliUtil.ProviderApps.list({ server: server, org: providerOrg.id }).then(function(providerApps) {
      debug('apps: %j', providerApps);
      var providerApp = _.filter(providerApps, { name: appName });
      if (providerApp.length !== 1) {
        // make a new provider-app if this is bluemix
        if (providerOrg.bluemix) {
          return cliUtil.ProviderApps.create({
            app: appName,
            title: appTitle,
            server: server,
            org: providerOrg.id,
            runtime: 'bluemix',
          });
        }
        return null;
      }
      return providerApp[0];
    });
  });

  return Promise.all([ providerOrgPromise, providerAppPromise ]);
}
