// # Mail
// Handles sending email for Ghost
const _ = require('lodash');
const Promise = require('bluebird');
const validator = require('validator');
const config = require('../../../shared/config');
const errors = require('@tryghost/errors');
const {i18n} = require('../../lib/common');
const settingsCache = require('../settings/cache');
const urlUtils = require('../../../shared/url-utils');

const helpMessage = i18n.t('errors.api.authentication.checkEmailConfigInstructions', {url: 'https://ghost.org/docs/concepts/config/#mail'});
const defaultErrorMessage = i18n.t('errors.mail.failedSendingEmail.error');

function getDomain() {
    const domain = urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
    return domain && domain[1];
}

function getFromAddress(requestedFromAddress) {
    const configAddress = config.get('mail') && config.get('mail').from;

    const address = requestedFromAddress || configAddress;
    // If we don't have a from address at all
    if (!address) {
        // Default to noreply@[blog.url]
        return getFromAddress(`noreply@${getDomain()}`);
    }

    // If we do have a from address, and it's just an email
    if (validator.isEmail(address, {require_tld: false})) {
        const defaultBlogTitle = settingsCache.get('title') ? settingsCache.get('title').replace(/"/g, '\\"') : i18n.t('common.mail.title', {domain: getDomain()});
        return `"${defaultBlogTitle}" <${address}>`;
    }

    return address;
}

function createMessage(message) {
    const encoding = 'base64';
    const generateTextFromHTML = !message.forceTextContent;
    return Object.assign({}, message, {
        from: getFromAddress(message.from),
        generateTextFromHTML,
        encoding
    });
}

function createMailError({message, err, ignoreDefaultMessage} = {message: ''}) {
    const fullErrorMessage = defaultErrorMessage + message;
    let statusCode = (err && err.name === 'RecipientError') ? 400 : 500;
    return new errors.EmailError({
        message: ignoreDefaultMessage ? message : fullErrorMessage,
        err: err,
        statusCode,
        help: helpMessage
    });
}

module.exports = class GhostMailer {
    constructor() {
        const nodemailer = require('nodemailer');
        const transport = config.get('mail') && config.get('mail').transport || 'direct';
        // nodemailer mutates the options passed to createTransport
        const options = config.get('mail') && _.clone(config.get('mail').options) || {};

        this.state = {
            usingDirect: transport === 'direct'
        };
        this.transport = nodemailer.createTransport(transport, options);
    }

    send(message) {
        if (!(message && message.subject && message.html && message.to)) {
            return Promise.reject(createMailError({
                message: i18n.t('errors.mail.incompleteMessageData.error'),
                ignoreDefaultMessage: true
            }));
        }

        const messageToSend = createMessage(message);

        return this.sendMail(messageToSend).then((response) => {
            if (this.transport.transportType === 'DIRECT') {
                return this.handleDirectTransportResponse(response);
            }
            return response;
        });
    }

    sendMail(message) {
        return new Promise((resolve, reject) => {
            this.transport.sendMail(message, (err, response) => {
                if (err) {
                    reject(createMailError({
                        message: i18n.t('errors.mail.reason', {reason: err.message || err}),
                        err
                    }));
                }
                resolve(response);
            });
        });
    }

    handleDirectTransportResponse(response) {
        return new Promise((resolve, reject) => {
            response.statusHandler.once('failed', function (data) {
                if (data.error && data.error.errno === 'ENOTFOUND') {
                    reject(createMailError({
                        message: i18n.t('errors.mail.noMailServerAtAddress.error', {domain: data.domain})
                    }));
                }

                reject(createMailError());
            });

            response.statusHandler.once('requeue', function (data) {
                if (data.error && data.error.message) {
                    reject(createMailError({
                        message: i18n.t('errors.mail.reason', {reason: data.error.message})
                    }));
                }

                reject(createMailError());
            });

            response.statusHandler.once('sent', function () {
                resolve(i18n.t('notices.mail.messageSent'));
            });
        });
    }
};
