// # DB API
// API for DB operations
const Promise = require('bluebird'),
    _ = require('lodash'),
    pipeline = require('../../lib/promise/pipeline'),
    localUtils = require('./utils'),
    exporter = require('../../data/exporter'),
    importer = require('../../data/importer'),
    backupDatabase = require('../../data/db/backup'),
    models = require('../../models'),
    common = require('../../lib/common'),
    docName = 'db';

let db;

/**
 * ## DB API Methods
 *
 * **See:** [API Methods](constants.js.html#api%20methods)
 */
db = {
    /**
     * ### Archive Content
     * Generate the JSON to export
     *
     * @public
     * @returns {Promise} Ghost Export JSON format
     */
    backupContent(options) {
        let tasks;

        options = options || {};

        function jsonResponse(filename) {
            return {db: [{filename: filename}]};
        }

        tasks = [
            localUtils.convertOptions(exporter.EXCLUDED_TABLES, null, {forModel: false}),
            backupDatabase,
            jsonResponse
        ];

        return pipeline(tasks, options);
    },
    /**
     * ### Export Content
     * Generate the JSON to export
     *
     * @public
     * @param {{context}} options
     * @returns {Promise} Ghost Export JSON format
     */
    exportContent(options) {
        let tasks;

        options = options || {};

        // Export data, otherwise send error 500
        function exportContent(options) {
            return exporter.doExport({include: options.include}).then((exportedData) => {
                return {
                    db: [exportedData]
                };
            }).catch((err) => {
                return Promise.reject(new common.errors.GhostError({err: err}));
            });
        }

        tasks = [
            localUtils.handlePermissions(docName, 'exportContent'),
            localUtils.convertOptions(exporter.EXCLUDED_TABLES, null, {forModel: false}),
            exportContent
        ];

        return pipeline(tasks, options);
    },
    /**
     * ### Import Content
     * Import posts, tags etc from a JSON blob
     *
     * @public
     * @param {{context}} options
     * @returns {Promise} Success
     */
    importContent(options) {
        let tasks;
        options = options || {};

        function importContent(options) {
            return importer.importFromFile(_.omit(options, 'include'), {include: options.include})
                // NOTE: response can contain 2 objects if images are imported
                .then((response) => {
                    return {
                        db: [],
                        problems: response.length === 2 ? response[1].problems : response[0].problems
                    };
                });
        }

        tasks = [
            localUtils.handlePermissions(docName, 'importContent'),
            localUtils.convertOptions(exporter.EXCLUDED_TABLES, null, {forModel: false}),
            importContent
        ];

        return pipeline(tasks, options);
    },
    /**
     * ### Delete All Content
     * Remove all posts and tags
     *
     * @public
     * @param {{context}} options
     * @returns {Promise} Success
     */
    deleteAllContent(options) {
        let tasks;
        const queryOpts = {columns: 'id', context: {internal: true}, destroyAll: true};

        options = options || {};

        /**
         * @NOTE:
         * We fetch all posts with `columns:id` to increase the speed of this endpoint.
         * And if you trigger `post.destroy(..)`, this will trigger bookshelf and model events.
         * But we only have to `id` available in the model. This won't work, because:
         *   - model layer can't trigger event e.g. `post.page` to trigger `post|page.unpublished`.
         *   - `onDestroyed` or `onDestroying` can contain custom logic
         */
        function deleteContent() {
            return models.Base.transaction((transacting) => {
                queryOpts.transacting = transacting;

                return models.Post.findAll(queryOpts)
                    .then((response) => {
                        return Promise.map(response.models, (post) => {
                            return models.Post.destroy(Object.assign({id: post.id}, queryOpts));
                        }, {concurrency: 100});
                    })
                    .then(() => {
                        return models.Tag.findAll(queryOpts);
                    })
                    .then((response) => {
                        return Promise.map(response.models, (tag) => {
                            return models.Tag.destroy(Object.assign({id: tag.id}, queryOpts));
                        }, {concurrency: 100});
                    })
                    .return({db: []})
                    .catch((err) => {
                        throw new common.errors.GhostError({
                            err: err
                        });
                    });
            });
        }

        tasks = [
            localUtils.handlePermissions(docName, 'deleteAllContent'),
            backupDatabase,
            deleteContent
        ];

        return pipeline(tasks, options);
    }
};

module.exports = db;