const _debug = require('ghost-ignition').debug._base;
const debug = _debug('ghost-query');
const _ = require('lodash');

module.exports = function (Bookshelf) {
    const modelProto = Bookshelf.Model.prototype;
    const countQueryBuilder = {
        tags: {
            posts: function addPostCountToTags(model, options) {
                model.query('columns', 'tags.*', function (qb) {
                    qb.count('posts.id')
                        .from('posts')
                        .leftOuterJoin('posts_tags', 'posts.id', 'posts_tags.post_id')
                        .whereRaw('posts_tags.tag_id = tags.id')
                        .as('count__posts');

                    if (options.context && options.context.public) {
                        // @TODO use the filter behavior for posts
                        qb.andWhere('posts.type', '=', 'post');
                        qb.andWhere('posts.status', '=', 'published');
                    }
                });
            }
        },
        users: {
            posts: function addPostCountToUsers(model, options) {
                model.query('columns', 'users.*', function (qb) {
                    qb.count('posts.id')
                        .from('posts')
                        .join('posts_authors', 'posts.id', 'posts_authors.post_id')
                        .whereRaw('posts_authors.author_id = users.id')
                        .as('count__posts');

                    if (options.context && options.context.public) {
                        // @TODO use the filter behavior for posts
                        qb.andWhere('posts.type', '=', 'post');
                        qb.andWhere('posts.status', '=', 'published');
                    }
                });
            }
        }
    };

    const Model = Bookshelf.Model.extend({
        addCounts: function (options) {
            if (!options) {
                return;
            }

            const tableName = _.result(this, 'tableName');

            if (options.withRelated && options.withRelated.indexOf('count.posts') > -1) {
                // remove post_count from withRelated
                options.withRelated = _.pull([].concat(options.withRelated), 'count.posts');

                // Call the query builder
                countQueryBuilder[tableName].posts(this, options);
            }
        },
        fetch: function () {
            this.addCounts.apply(this, arguments);

            // Useful when debugging no. database queries, GQL, etc
            // To output this, use DEBUG=ghost:*,ghost-query
            if (_debug.enabled('ghost-query')) {
                debug('QUERY', this.query().toQuery());
            }

            // Call parent fetch
            return modelProto.fetch.apply(this, arguments);
        },
        fetchAll: function () {
            this.addCounts.apply(this, arguments);

            // Useful when debugging no. database queries, GQL, etc
            // To output this, use DEBUG=ghost:*,ghost-query
            if (_debug.enabled('ghost-query')) {
                debug('QUERY', this.query().toQuery());
            }

            // Call parent fetchAll
            return modelProto.fetchAll.apply(this, arguments);
        },

        serialize: function serialize(options) {
            const attrs = modelProto.serialize.call(this, options);
            const countRegex = /^(count)(__)(.*)$/;

            _.forOwn(attrs, function (value, key) {
                const match = key.match(countRegex);
                if (match) {
                    attrs[match[1]] = attrs[match[1]] || {};
                    attrs[match[1]][match[3]] = value;
                    delete attrs[key];
                }
            });

            return attrs;
        }
    });

    Bookshelf.Model = Model;
};
