const _ = require('lodash');
const config = require('../../../shared/config');
const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');
const logging = require('@tryghost/logging');
const moment = require('moment');
const models = require('../../models');
const mail = require('../mail');

const messages = {
    setupAlreadyCompleted: 'Setup has already been completed.',
    setupMustBeCompleted: 'Setup must be completed before making this request.',
    setupUnableToRun: 'Database missing fixture data. Please reset database and try again.',
    sampleBlogDescription: 'Thoughts, stories and ideas.',
    yourNewGhostBlog: 'Your New Ghost Site',
    unableToSendWelcomeEmail: 'Unable to send welcome email, your site will continue to function.',
    failedThemeInstall: 'Theme {themeName} didn\'t install because of the error: {error}'
};

const postSetupFixtures = {
    'coming-soon': '{"version":"0.3.1","atoms":[],"cards":[],"markups":[["a",["href","#/portal/"]]],"sections":[[1,"p",[[0,[],0,"This is {{site.title}}, a brand new site by {{author.name}} that\'s just getting started. Things will be up and running here shortly, but you can "],[0,[0],1,"subscribe"],[0,[],0," in the meantime if you\'d like to stay up to date and receive emails when new content is published!"]]]],"ghostVersion":"4.0"}',
    about: '{"version":"0.3.1","atoms":[],"cards":[["hr",{}]],"markups":[["a",["href","https://ghost.org"]]],"sections":[[1,"p",[[0,[],0,"{{site.title}} is an independent publication launched in {{date}} by {{author.name}}. If you subscribe today, you\'ll get full access to the website as well as email newsletters about new content when it\'s available. Your subscription makes this site possible, and allows {{site.title}} to continue to exist. Thank you!"]]],[1,"h3",[[0,[],0,"Access all areas"]]],[1,"p",[[0,[],0,"By signing up, you\'ll get access to the full archive of everything that\'s been published before and everything that\'s still to come. Your very own private library."]]],[1,"h3",[[0,[],0,"Fresh content, delivered"]]],[1,"p",[[0,[],0,"Stay up to date with new content sent straight to your inbox! No more worrying about whether you missed something because of a pesky algorithm or news feed."]]],[1,"h3",[[0,[],0,"Meet people like you"]]],[1,"p",[[0,[],0,"Join a community of other subscribers who share the same interests."]]],[10,0],[1,"h3",[[0,[],0,"Start your own thing"]]],[1,"p",[[0,[],0,"Enjoying the experience? Get started for free and set up your very own subscription business using "],[0,[0],1,"Ghost"],[0,[],0,", the same platform that powers this website."]]]],"ghostVersion":"4.0"}'
};

/**
 * Returns setup status
 *
 * @return {Promise<Boolean>}
 */
async function checkIsSetup() {
    return models.User.isSetup();
}

/**
 * Allows an assertion to be made about setup status.
 *
 * @param  {Boolean} status True: setup must be complete. False: setup must not be complete.
 * @return {Function} returns a "task ready" function
 */
function assertSetupCompleted(status) {
    return async function checkPermission(__) {
        const isSetup = await checkIsSetup();

        if (isSetup === status) {
            return __;
        }

        const completed = tpl(messages.setupAlreadyCompleted);
        const notCompleted = tpl(messages.setupMustBeCompleted);

        function throwReason(reason) {
            throw new errors.NoPermissionError({message: reason});
        }

        if (isSetup) {
            throwReason(completed);
        } else {
            throwReason(notCompleted);
        }
    };
}

async function setupUser(userData) {
    const context = {context: {internal: true}};

    const owner = await models.User.findOne({role: 'Owner', status: 'all'});

    if (!owner) {
        throw new errors.InternalServerError({
            message: tpl(messages.setupUnableToRun)
        });
    }

    const user = await models.User.setup(userData, _.extend({id: owner.id}, context));

    return {
        user: user,
        userData: userData
    };
}

async function doSettings(data, settingsAPI) {
    const context = {context: {user: data.user.id}};
    const user = data.user;
    const blogTitle = data.userData.blogTitle;
    const description = data.userData.description ? data.userData.description.trim() : null;

    let userSettings;

    if (!blogTitle || typeof blogTitle !== 'string') {
        return user;
    }

    userSettings = [
        {key: 'title', value: blogTitle.trim()},
        {key: 'description', value: description || tpl(messages.sampleBlogDescription)}
    ];

    if (data.userData.accentColor) {
        userSettings.push({
            key: 'accent_color', value: data.userData.accentColor
        });
    }

    await settingsAPI.edit({settings: userSettings}, context);

    return user;
}

// Update names for default product and newsletter to site title
async function doTiersAndNewsletter(data, api) {
    const {tiers: tiersAPI, newsletters: newslettersAPI} = api;
    const context = {context: {user: data.user.id}};
    const user = data.user;
    const blogTitle = data.userData.blogTitle;

    if (!blogTitle || typeof blogTitle !== 'string') {
        return user;
    }
    try {
        const tierPage = await tiersAPI.browse({limit: 'all'});
        const newsletterPage = await newslettersAPI.browse({limit: 'all'});

        const defaultTier = tierPage.tiers.find(p => p.slug === 'default-product');
        const defaultNewsletter = newsletterPage.newsletters.find(p => p.slug === 'default-newsletter');

        if (defaultTier) {
            await tiersAPI.edit({tiers: [{
                name: blogTitle.trim()
            }]}, {context: context.context, id: defaultTier.id});
        }

        if (defaultNewsletter) {
            await newslettersAPI.edit({newsletters: [{
                name: blogTitle.trim()
            }]}, {context: context.context, id: defaultNewsletter.id});
        }
    } catch (e) {
        return data;
    }

    return data;
}

async function doFixtures(data) {
    const date = moment().format('MMMM YYYY');

    _.each(postSetupFixtures, async (mobiledoc, key) => {
        // Using very simple find and replace because we control the fixtures
        mobiledoc = mobiledoc.replace(/{{site.title}}/g, data.userData.blogTitle);
        mobiledoc = mobiledoc.replace(/{{author.name}}/g, data.userData.name);
        mobiledoc = mobiledoc.replace(/{{date}}/, date);

        const post = await models.Post.findOne({slug: key});

        if (post) {
            await models.Post.edit({mobiledoc}, {id: post.id});
        }
    });

    return data;
}

function sendWelcomeEmail(email, mailAPI) {
    if (config.get('sendWelcomeEmail')) {
        const data = {
            ownerEmail: email
        };

        return mail.utils.generateContent({data: data, template: 'welcome'})
            .then(async (content) => {
                const message = {
                    to: email,
                    subject: tpl(messages.yourNewGhostBlog),
                    html: content.html,
                    text: content.text
                };

                const payload = {
                    mail: [{
                        message: message,
                        options: {}
                    }]
                };

                await mailAPI.send(payload, {context: {internal: true}})
                    .catch((err) => {
                        err.context = tpl(messages.unableToSendWelcomeEmail);
                        logging.error(err);
                    });
            });
    }
    return Promise.resolve();
}

async function installTheme(data, api) {
    const {theme: themeName} = data.userData;

    if (!themeName) {
        return data;
    }

    if (themeName.toLowerCase() === 'tryghost/source') {
        logging.warn('Skipping theme install as Source is the default theme.');
        return data;
    }

    // Use the api instead of the services as the api performs extra logic
    try {
        const installResults = await api.themes.install({
            source: 'github',
            ref: themeName,
            context: {internal: true}
        });
        const theme = installResults.themes[0];

        await api.themes.activate({
            name: theme.name,
            context: {internal: true}
        });
    } catch (error) {
        //Fallback to Casper/Source by doing nothing as the theme setting update is the last step
        logging.warn(tpl(messages.failedThemeInstall, {themeName, error: error.message}));
    }

    return data;
}

module.exports = {
    checkIsSetup: checkIsSetup,
    assertSetupCompleted: assertSetupCompleted,
    setupUser: setupUser,
    doSettings: doSettings,
    doProductAndNewsletter: doTiersAndNewsletter,
    installTheme: installTheme,
    doFixtures: doFixtures,
    sendWelcomeEmail: sendWelcomeEmail
};
