import typing

import bson
import pymongo
from flask import Blueprint, current_app

from pillar.api.utils import jsonify

blueprint = Blueprint('latest', __name__)


def _public_project_ids() -> typing.List[bson.ObjectId]:
    """Returns a list of ObjectIDs of public projects.

    Memoized in setup_app().
    """

    proj_coll = current_app.db('projects')
    result = proj_coll.find({'is_private': False}, {'_id': 1})
    return [p['_id'] for p in result]


def latest_nodes(db_filter, projection, limit):
    """Returns the latest nodes, of a certain type, of public projects.

    Also includes information about the project and the user of each node.
    """

    proj = {
        '_created': 1,
        '_updated': 1,
        'project._id': 1,
        'project.url': 1,
        'project.name': 1,
        'name': 1,
        'node_type': 1,
        'parent': 1,
        **projection,
    }

    nodes_coll = current_app.db('nodes')
    pipeline = [
        {'$match': {'_deleted': {'$ne': True}}},
        {'$match': db_filter},
        {'$match': {'project': {'$in': _public_project_ids()}}},
        {'$sort': {'_created': pymongo.DESCENDING}},
        {'$limit': limit},
        {'$lookup': {"from": "users",
                     "localField": "user",
                     "foreignField": "_id",
                     "as": "user"}},
        {'$unwind': {'path': "$user"}},
        {'$lookup': {"from": "projects",
                     "localField": "project",
                     "foreignField": "_id",
                     "as": "project"}},
        {'$unwind': {'path': "$project"}},
        {'$project': proj},
    ]

    latest = nodes_coll.aggregate(pipeline)
    return list(latest)


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

    return jsonify({'_items': latest})


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

    # Embed the comments' parents.
    # TODO: move to aggregation pipeline.
    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

    return jsonify({'_items': latest})


def setup_app(app, url_prefix):
    global _public_project_ids

    app.register_api_blueprint(blueprint, url_prefix=url_prefix)
    cached = app.cache.cached(timeout=3600)
    _public_project_ids = cached(_public_project_ids)
