'use strict';

var SMTPConnection = require('smtp-connection');
var packageData = require('../package.json');
var wellknown = require('nodemailer-wellknown');
var clone = require('clone');
var urllib = require('url');

var EventEmitter = require('events').EventEmitter;
var util = require('util');

// expose to the world
module.exports = function (options) {
    return new SMTPTransport(options);
};

/**
 * Creates a SMTP transport object for Nodemailer
 *
 * @constructor
 * @param {Object} options Connection options
 */
function SMTPTransport(options) {
    EventEmitter.call(this);

    var hostData;

    if (options && typeof options === 'string') {
        options = {
            url: options
        };
    }

    this.options = options && clone(options) || {};

    if (this.options.service && (hostData = wellknown(this.options.service))) {
        Object.keys(hostData).forEach(function (key) {
            if (!(key in this.options)) {
                this.options[key] = hostData[key];
            }
        }.bind(this));
    }

    // parse a configuration URL into configuration options
    if (this.options.url) {
        [urllib.parse(this.options.url, true)].forEach(function (url) {
            var auth;

            this.options.secure = url.protocol === 'smtps:';

            if (!isNaN(url.port) && Number(url.port)) {
                this.options.port = Number(url.port);
            }

            if (url.hostname) {
                this.options.host = url.hostname;
            }

            if (url.auth) {
                auth = url.auth.split(':');

                if (!this.options.auth) {
                    this.options.auth = {};
                }

                this.options.auth.user = decodeURIComponent(auth[0]);
                this.options.auth.pass = decodeURIComponent(auth[1]);
            }

            Object.keys(url.query || {}).forEach(function (key) {
                if (!(key in this.options)) {
                    this.options[key] = url.query[key];
                }
            }.bind(this));
        }.bind(this));
    }

    // temporary object
    var connection = new SMTPConnection(this.options);

    this.name = 'SMTP';
    this.version = packageData.version + '[client:' + connection.version + ']';
}
util.inherits(SMTPTransport, EventEmitter);

/**
 * Sends an e-mail using the selected settings
 *
 * @param {Object} mail Mail object
 * @param {Function} callback Callback function
 */
SMTPTransport.prototype.send = function (mail, callback) {
    var connection = new SMTPConnection(this.options);
    var returned = false;

    connection.on('log', function (log) {
        this.emit('log', log);
    }.bind(this));

    connection.once('error', function (err) {
        if (returned) {
            return;
        }
        returned = true;
        connection.close();
        return callback(err);
    });

    connection.once('end', function () {
        if (returned) {
            return;
        }
        returned = true;
        return callback(new Error('Connection closed'));
    });

    var sendMessage = function () {
        connection.send(mail.data.envelope || mail.message.getEnvelope(), mail.message.createReadStream(), function (err, info) {
            var envelope;

            if (returned) {
                return;
            }
            returned = true;

            connection.close();
            if (err) {
                return callback(err);
            }
            envelope = mail.data.envelope || mail.message.getEnvelope();
            info.envelope = {
                from: envelope.from,
                to: envelope.to
            };
            info.messageId = (mail.message.getHeader('message-id') || '').replace(/[<>\s]/g, '');
            return callback(null, info);
        });
    };

    connection.connect(function () {
        if (returned) {
            return;
        }

        if (this.options.auth) {
            connection.login(this.options.auth, function (err) {
                if (returned) {
                    return;
                }

                if (err) {
                    returned = true;
                    connection.close();
                    return callback(err);
                }

                sendMessage();
            });
        } else {
            sendMessage();
        }
    }.bind(this));
};
