const _ = require('lodash');
const hbs = require('./engine');
const urlUtils = require('../../../shared/url-utils');
const config = require('../../../shared/config');
const {i18n, api} = require('../proxy');
const errors = require('@tryghost/errors');
const settingsCache = require('../../../server/services/settings/cache');
const labs = require('../../../server/services/labs');
const activeTheme = require('./active');
const preview = require('./preview');

// ### Ensure Active Theme
// Ensure there's a properly set & mounted active theme before attempting to serve a site request
// If there is no active theme, throw an error
// Else, ensure the active theme is mounted
function ensureActiveTheme(req, res, next) {
    // CASE: this means that the theme hasn't been loaded yet i.e. there is no active theme
    if (!activeTheme.get()) {
        // This is the one place we ACTUALLY throw an error for a missing theme as it's a request we cannot serve
        return next(new errors.InternalServerError({
            // We use the settingsCache here, because the setting will be set,
            // even if the theme itself is not usable because it is invalid or missing.
            message: i18n.t('errors.middleware.themehandler.missingTheme', {theme: settingsCache.get('active_theme')})
        }));
    }

    // CASE: bootstrap theme validation failed, we would like to show the errors on the site [only production]
    if (activeTheme.get().error && config.get('env') === 'production') {
        return next(new errors.InternalServerError({
            // We use the settingsCache here, because the setting will be set,
            // even if the theme itself is not usable because it is invalid or missing.
            message: i18n.t('errors.middleware.themehandler.invalidTheme', {theme: settingsCache.get('active_theme')}),
            errorDetails: activeTheme.get().error.errorDetails
        }));
    }

    // If the active theme has not yet been mounted, mount it into express
    if (!activeTheme.get().mounted) {
        activeTheme.get().mount(req.app);
    }

    next();
}

/*
 * @TODO
 * This should be definitely refactored and we need to consider _some_
 * members settings as publicly readable
 */
async function haxGetMembersPriceData() {
    const defaultPrice = {
        amount: 0,
        currency: 'usd',
        interval: 'year',
        nickname: ''
    };

    function makePriceObject(price) {
        const numberAmount = 0 + price.amount;
        const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
        return {
            valueOf() {
                return dollarAmount;
            },
            amount: numberAmount,
            currency: price.currency,
            nickname: price.name,
            interval: price.interval
        };
    }

    try {
        const {products} = await api.canary.products.browse({
            include: 'stripe_prices'
        });

        const defaultProduct = products[0];

        const monthlyPriceId = settingsCache.get('members_monthly_price_id');
        const yearlyPriceId = settingsCache.get('members_yearly_price_id');

        const activePrices = defaultProduct.stripe_prices.filter((price) => {
            return price.active;
        });

        const nonZeroPrices = activePrices.filter((price) => {
            return price.amount !== 0;
        });

        const monthlyPrice = nonZeroPrices.find((price) => {
            return price.id === monthlyPriceId;
        });

        const yearlyPrice = nonZeroPrices.find((price) => {
            return price.id === yearlyPriceId;
        });

        const priceData = {
            monthly: makePriceObject(monthlyPrice || defaultPrice),
            yearly: makePriceObject(yearlyPrice || defaultPrice),
            currency: monthlyPrice ? monthlyPrice.currency : defaultPrice.currency
        };

        return priceData;
    } catch (err) {
        return {
            monthly: makePriceObject(defaultPrice),
            yearly: makePriceObject(defaultPrice),
            currency: 'usd'
        };
    }
}

function getSiteData(req) {
    let siteData = settingsCache.getPublic();

    // @TODO: it would be nicer if this was proper middleware somehow...
    siteData = preview.handle(req, siteData);

    // theme-only computed property added to @site
    if (settingsCache.get('members_signup_access') === 'none') {
        const escapedUrl = encodeURIComponent(urlUtils.urlFor({relativeUrl: '/rss/'}, true));
        siteData.signup_url = `https://feedly.com/i/subscription/feed/${escapedUrl}`;
    } else {
        siteData.signup_url = '#/portal';
    }

    return siteData;
}

async function updateGlobalTemplateOptions(req, res, next) {
    // Static information, same for every request unless the settings change
    // @TODO: bind this once and then update based on events?
    // @TODO: decouple theme layer from settings cache using the Content API
    const siteData = getSiteData(req);
    const labsData = labs.getAll();

    const themeData = {
        posts_per_page: activeTheme.get().config('posts_per_page'),
        image_sizes: activeTheme.get().config('image_sizes')
    };
    const priceData = await haxGetMembersPriceData();

    // @TODO: only do this if something changed?
    // @TODO: remove blog in a major where we are happy to break more themes
    {
        hbs.updateTemplateOptions({
            data: {
                blog: siteData,
                site: siteData,
                labs: labsData,
                config: themeData,
                price: priceData
            }
        });
    }

    next();
}

function updateLocalTemplateData(req, res, next) {
    // Pass 'secure' flag to the view engine
    // so that templates can choose to render https or http 'url', see url utility
    res.locals.secure = req.secure;

    next();
}

function updateLocalTemplateOptions(req, res, next) {
    const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals);
    const siteData = {
        url: urlUtils.urlFor('home', {secure: req.secure, trailingSlash: false}, true)
    };

    const member = req.member ? {
        uuid: req.member.uuid,
        email: req.member.email,
        name: req.member.name,
        firstname: req.member.name && req.member.name.split(' ')[0],
        avatar_image: req.member.avatar_image,
        subscriptions: req.member.subscriptions && req.member.subscriptions.map((sub) => {
            return Object.assign({}, sub, {
                default_payment_card_last4: sub.default_payment_card_last4 || '****'
            });
        }),
        paid: req.member.status !== 'free'
    } : null;

    hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
        data: {
            member: member,
            site: siteData,
            // @deprecated: a gscan warning for @blog was added before 3.0 which replaced it with @site
            blog: siteData
        }
    }));

    next();
}

module.exports = [
    ensureActiveTheme,
    updateGlobalTemplateOptions,
    updateLocalTemplateData,
    updateLocalTemplateOptions
];
