import itertools

import pymongo
from flask import Blueprint, current_app

from pillar.api.utils import jsonify

blueprint = Blueprint('latest', __name__)


def keep_fetching(collection, db_filter, projection, sort, py_filter,
                  batch_size=12):
    """Yields results for which py_filter returns True"""

    projection['_deleted'] = 1
    curs = collection.find(db_filter, projection).sort(sort)
    curs.batch_size(batch_size)

    for doc in curs:
        if doc.get('_deleted'):
            continue
        doc.pop('_deleted', None)
        if py_filter(doc):
            yield doc


def latest_nodes(db_filter, projection, py_filter, limit):
    nodes = current_app.data.driver.db['nodes']

    proj = {
        '_created': 1,
        '_updated': 1,
    }
    proj.update(projection)

    latest = keep_fetching(nodes, db_filter, proj,
                           [('_created', pymongo.DESCENDING)],
                           py_filter, limit)

    result = list(itertools.islice(latest, limit))
    return result


def has_public_project(node_doc):
    """Returns True iff the project the node belongs to is public."""

    project_id = node_doc.get('project')
    return is_project_public(project_id)


# TODO: cache result, for a limited amt. of time, or for this HTTP request.
def is_project_public(project_id):
    """Returns True iff the project is public."""

    project = current_app.data.driver.db['projects'].find_one(project_id)
    if not project:
        return False

    return not project.get('is_private')


@blueprint.route('/assets')
def latest_assets():
    latest = latest_nodes({'node_type': 'asset',
                           'properties.status': 'published'},
                          {'name': 1, 'project': 1, 'user': 1, 'node_type': 1,
                           'parent': 1, 'picture': 1, 'properties.status': 1,
                           'properties.content_type': 1,
                           'permissions.world': 1},
                          has_public_project, 12)

    embed_user(latest)
    embed_project(latest)

    return jsonify({'_items': latest})


def embed_user(latest):
    users = current_app.data.driver.db['users']

    for comment in latest:
        user_id = comment['user']
        comment['user'] = users.find_one(user_id, {
            'auth': 0, 'groups': 0, 'roles': 0, 'settings': 0, 'email': 0,
            '_created': 0, '_updated': 0, '_etag': 0})


def embed_project(latest):
    projects = current_app.data.driver.db['projects']

    for comment in latest:
        project_id = comment['project']
        comment['project'] = projects.find_one(project_id, {'_id': 1, 'name': 1,
                                                            'url': 1})


@blueprint.route('/comments')
def latest_comments():
    latest = latest_nodes({'node_type': 'comment',
                           'properties.status': 'published'},
                          {'project': 1, 'parent': 1, 'user': 1,
                           'properties.content': 1, 'node_type': 1,
                           'properties.status': 1,
                           'properties.is_reply': 1},
                          has_public_project, 10)

    # Embed the comments' parents.
    nodes = current_app.data.driver.db['nodes']
    parents = {}
    for comment in latest:
        parent_id = comment['parent']

        if parent_id in parents:
            comment['parent'] = parents[parent_id]
            continue

        parent = nodes.find_one(parent_id)
        parents[parent_id] = parent
        comment['parent'] = parent

    embed_project(latest)
    embed_user(latest)

    return jsonify({'_items': latest})


def setup_app(app, url_prefix):
    app.register_api_blueprint(blueprint, url_prefix=url_prefix)
