'use strict';

var PromiseA;
try {
  PromiseA = require('bluebird');
} catch (e) {
  PromiseA = global.Promise;
}

// opts.approveDomains(options, certs, cb)
module.exports.create = function (opts) {
  // accept all defaults for greenlock.challenges, greenlock.store, greenlock.middleware
  if (!opts._communityPackage) {
    opts._communityPackage = 'greenlock-express.js';
    opts._communityPackageVersion = require('./package.json').version;
  }

  function explainError(e) {
    console.error('Error:' + e.message);
    if ('EACCES' === e.errno) {
      console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'.");
      console.error("You probably need to use \"sudo\" or \"sudo setcap 'cap_net_bind_service=+ep' $(which node)\"");
      return;
    }
    if ('EADDRINUSE' === e.errno) {
      console.error("'" + e.address + ":" + e.port + "' is already being used by some other program.");
      console.error("You probably need to stop that program or restart your computer.");
      return;
    }
    console.error(e.code + ": '" + e.address + ":" + e.port + "'");
  }

  function _listen(plainPort, plain) {
    if (!plainPort) {
      plainPort = 80;
    }

    var parts = String(plainPort).split(':');
    var p = parts.pop();
    var addr = parts.join(':').replace(/^\[/, '').replace(/\]$/, '');
    var args = [];
    var httpType;
    var server;
    var validHttpPort = (parseInt(p, 10) >= 0);

    function tryPlain() {
      server = require('http').createServer(
        greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')()))
      );
      httpType = 'http';
    }

    function trySecure() {
      var https;
      try {
        https = require('spdy');
        greenlock.tlsOptions.spdy = {
          protocols: ['h2', 'http/1.1'],
          plain: false
        };
        httpType = 'http2 (spdy/h2)';
      } catch (e) {
        https = require('https');
        httpType = 'https';
      }
      server = https.createServer(
        greenlock.tlsOptions, greenlock.middleware.sanitizeHost(function (req, res) {
          try {
            greenlock.app(req, res);
          } catch (e) {
            console.error("[error] [greenlock.app] Your HTTP handler had an uncaught error:");
            console.error(e);
            try {
              res.statusCode = 500;
              res.end("Internal Server Error: [Greenlock] HTTP exception logged for user-provided handler.");
            } catch (e) {
              // ignore
              // (headers may have already been sent, etc)
            }
          }
        })
      );
      server.type = httpType;
    }

    if (addr) {
      args[1] = addr;
    }
    if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) {
      console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe");
    }
    if (plain) {
      tryPlain();
    } else {
      trySecure();
    }

    var promise = new PromiseA(function (resolve) {
      args[0] = p;
      args.push(function () {
        resolve(server);
      });
      server.listen.apply(server, args).on('error', function (e) {
        if (server.listenerCount('error') < 2) {
          console.warn("Did not successfully create http server and bind to port '" + p + "':");
          explainError(e);
          process.exit(41);
        }
      });
    });

    promise.server = server;
    return promise;
  }

  // NOTE: 'greenlock' is just 'opts' renamed
  var greenlock = require('greenlock').create(opts);

  if (!opts.app) {
    opts.app = function (req, res) {
      res.end("Hello, World!\nWith Love,\nGreenlock for Express.js");
    };
  }

  opts.listen = function (plainPort, port, fnPlain, fn) {
    var promises = [];
    var server;
    var plainServer;

    // If there is only one handler for the `listening` (i.e. TCP bound) event
    // then we want to use it as HTTPS (backwards compat)
    if (!fn) {
      fn = fnPlain;
      fnPlain = null;
    }

    promises.push(_listen(plainPort, true));
    promises.push(_listen(port, false));

    server = promises[1].server;
    plainServer = promises[0].server;

    PromiseA.all(promises).then(function () {
      // Report plain http status
      if ('function' === typeof fnPlain) {
        fnPlain.apply(plainServer);
      } else if (!fn && !plainServer.listenerCount('listening') && !server.listenerCount('listening')) {
        console.info('[:' + (plainServer.address().port || plainServer.address()) +
          "] Handling ACME challenges and redirecting to " + server.type);
      }

      // Report h2/https status
      if ('function' === typeof fn) {
        fn.apply(server);
      } else if (!server.listenerCount('listening')) {
        console.info('[:' + (server.address().port || server.address()) + "] Serving " + server.type);
      }
    });

    server.unencrypted = plainServer;
    return server;
  };
  opts.middleware.acme = function (opts) {
    return greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')(opts)));
  };
  opts.middleware.secure = function (app) {
    return greenlock.middleware.sanitizeHost(app);
  };

  return greenlock;
};