'use strict';

const assign = require('ember-cli-lodash-subset').assign;
const Command = require('../models/command');
const RSVP = require('rsvp');
const SilentError = require('silent-error');
const PortFinder = require('portfinder');
const Win = require('../utilities/windows-admin');
const EOL = require('os').EOL;

const Promise = RSVP.Promise;

PortFinder.basePort = 49153;

let getPort = RSVP.denodeify(PortFinder.getPort);
let defaultPort = process.env.PORT || 4200;

module.exports = Command.extend({
  name: 'serve',
  description: 'Builds and serves your app, rebuilding on file changes.',
  aliases: ['server', 's'],

  availableOptions: [
    { name: 'port',                 type: Number,  default: defaultPort,   aliases: ['p'],                                                     description: `To use a port different than ${defaultPort}. Pass 0 to automatically pick an available port.` },
    { name: 'host',                 type: String,                          aliases: ['H'],                                                     description: 'Listens on all interfaces by default' },
    { name: 'proxy',                type: String,                          aliases: ['pr', 'pxy'] },
    { name: 'secure-proxy',         type: Boolean, default: true,          aliases: ['spr'],                                                   description: 'Set to false to proxy self-signed SSL certificates' },
    { name: 'transparent-proxy',    type: Boolean, default: true,          aliases: ['transp'],                                                description: 'Set to false to omit x-forwarded-* headers when proxying' },
    { name: 'watcher',              type: String,  default: 'events',      aliases: ['w'] },
    { name: 'live-reload',          type: Boolean, default: true,          aliases: ['lr'] },
    { name: 'live-reload-host',     type: String,                          aliases: ['lrh'],                                                   description: 'Defaults to host' },
    { name: 'live-reload-base-url', type: String,                          aliases: ['lrbu'],                                                  description: 'Defaults to baseURL' },
    { name: 'live-reload-port',     type: Number,                          aliases: ['lrp'],                                                   description: '(Defaults to port number within [49152...65535])' },
    { name: 'environment',          type: String,  default: 'development', aliases: ['e', { 'dev': 'development' }, { 'prod': 'production' }], description: 'Possible values are "development", "production", and "test".' },
    { name: 'output-path',          type: 'Path',  default: 'dist/',       aliases: ['op', 'out'] },
    { name: 'ssl',                  type: Boolean, default: false },
    { name: 'ssl-key',              type: String,  default: 'ssl/server.key' },
    { name: 'ssl-cert',             type: String,  default: 'ssl/server.crt' },
  ],

  run(commandOptions) {
    commandOptions.liveReloadHost = commandOptions.liveReloadHost || commandOptions.host;

    return this._checkExpressPort(commandOptions)
      .then(this._autoFindLiveReloadPort.bind(this))
      .then(commandOptions => {
        let config = this.project.config(commandOptions.environment);

        this.ui.writeDeprecateLine(
          'Using the `baseURL` setting is deprecated, use `rootURL` instead.',
          !(!('rootURL' in config) && config.baseURL));

        this.ui.writeWarnLine(
          'The `baseURL` and `rootURL` settings should not be used at the same time.',
          !(('rootURL' in config) && config.baseURL));

        commandOptions = assign({}, commandOptions, {
          rootURL: config.rootURL,
          baseURL: config.baseURL || '/',
        });

        if (commandOptions.proxy) {
          if (!commandOptions.proxy.match(/^(http:|https:)/)) {
            let message = `You need to include a protocol with the proxy URL.${EOL}Try --proxy http://${commandOptions.proxy}`;

            return Promise.reject(new SilentError(message));
          }
        }

        return Win.checkIfSymlinksNeedToBeEnabled(this.ui)
          .then(() => this.runTask('Serve', commandOptions));
      });
  },

  _checkExpressPort(commandOptions) {
    return getPort({ port: commandOptions.port, host: commandOptions.host }).then(foundPort => {

      if (commandOptions.port !== foundPort && commandOptions.port !== 0) {
        let message = `Port ${commandOptions.port} is already in use.`;
        return Promise.reject(new SilentError(message));
      }

      // otherwise, our found port is good
      commandOptions.port = foundPort;
      return commandOptions;
    });
  },

  _autoFindLiveReloadPort(commandOptions) {
    return getPort({ port: commandOptions.liveReloadPort, host: commandOptions.liveReloadHost }).then(foundPort => {

      // if live reload port matches express port, try one higher
      if (foundPort === commandOptions.port) {
        commandOptions.liveReloadPort = foundPort + 1;
        return this._autoFindLiveReloadPort(commandOptions);
      }

      // port was already open
      if (foundPort === commandOptions.liveReloadPort) {
        return commandOptions;
      }

      // use found port as live reload port
      commandOptions.liveReloadPort = foundPort;
      return commandOptions;
    });
  },
});
