"""
Bookmarks Python API.
"""
from django.conf import settings

from eventtracking import tracker
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError

from . import DEFAULT_FIELDS, OPTIONAL_FIELDS
from .models import Bookmark
from .serializers import BookmarkSerializer


class BookmarksLimitReachedError(Exception):
    """
    if try to create new bookmark when max limit of bookmarks already reached
    """
    pass


def get_bookmark(user, usage_key, fields=None):
    """
    Return data for a bookmark.

    Arguments:
        user (User): The user of the bookmark.
        usage_key (UsageKey): The usage_key of the bookmark.
        fields (list): List of field names the data should contain (optional).

    Returns:
        Dict.

    Raises:
        ObjectDoesNotExist: If a bookmark with the parameters does not exist.
    """
    bookmarks_queryset = Bookmark.objects

    if len(set(fields or []) & set(OPTIONAL_FIELDS)) > 0:
        bookmarks_queryset = bookmarks_queryset.select_related('user', 'xblock_cache')
    else:
        bookmarks_queryset = bookmarks_queryset.select_related('user')

    bookmark = bookmarks_queryset.get(user=user, usage_key=usage_key)
    return BookmarkSerializer(bookmark, context={'fields': fields}).data


def get_bookmarks(user, course_key=None, fields=None, serialized=True):
    """
    Return data for bookmarks of a user.

    Arguments:
        user (User): The user of the bookmarks.
        course_key (CourseKey): The course_key of the bookmarks (optional).
        fields (list): List of field names the data should contain (optional).
            N/A if serialized is False.
        serialized (bool): Whether to return a queryset or a serialized list of dicts.
            Default is True.

    Returns:
         List of dicts if serialized is True else queryset.
    """
    bookmarks_queryset = Bookmark.objects.filter(user=user)

    if course_key:
        bookmarks_queryset = bookmarks_queryset.filter(course_key=course_key)

    if len(set(fields or []) & set(OPTIONAL_FIELDS)) > 0:
        bookmarks_queryset = bookmarks_queryset.select_related('user', 'xblock_cache')
    else:
        bookmarks_queryset = bookmarks_queryset.select_related('user')

    bookmarks_queryset = bookmarks_queryset.order_by('-created')

    if serialized:
        return BookmarkSerializer(bookmarks_queryset, context={'fields': fields}, many=True).data

    return bookmarks_queryset


def can_create_more(data):
    """
    Determine if a new Bookmark can be created for the course
    based on limit defined in django.conf.settings.MAX_BOOKMARKS_PER_COURSE

    Arguments:
        data (dict): The data to create the object with.
    Returns:
        Boolean
    """
    data = dict(data)

    user = data['user']
    course_key = data['usage_key'].course_key

    # User can create up to max_bookmarks_per_course bookmarks
    if Bookmark.objects.filter(user=user, course_key=course_key).count() >= settings.MAX_BOOKMARKS_PER_COURSE:
        return False

    return True


def create_bookmark(user, usage_key):
    """
    Create a bookmark.

    Arguments:
        user (User): The user of the bookmark.
        usage_key (UsageKey): The usage_key of the bookmark.

    Returns:
         Dict.

    Raises:
        ItemNotFoundError: If no block exists for the usage_key.
        BookmarksLimitReachedError: if try to create new bookmark when max limit of bookmarks already reached
    """

    usage_key = usage_key.replace(course_key=modulestore().fill_in_run(usage_key.course_key))
    data = {
        'user': user,
        'usage_key': usage_key
    }

    if usage_key.course_key.run is None:
        raise ItemNotFoundError

    if not can_create_more(data):
        raise BookmarksLimitReachedError

    bookmark, created = Bookmark.create(data)
    if created:
        _track_event('edx.bookmark.added', bookmark)
    return BookmarkSerializer(bookmark, context={'fields': DEFAULT_FIELDS + OPTIONAL_FIELDS}).data


def delete_bookmark(user, usage_key):
    """
    Delete a bookmark.

    Arguments:
        user (User): The user of the bookmark.
        usage_key (UsageKey): The usage_key of the bookmark.

    Returns:
         Dict.

    Raises:
        ObjectDoesNotExist: If a bookmark with the parameters does not exist.
    """
    bookmark = Bookmark.objects.get(user=user, usage_key=usage_key)
    bookmark.delete()
    _track_event('edx.bookmark.removed', bookmark)


def _track_event(event_name, bookmark):
    """
    Emit events for a bookmark.

    Arguments:
        event_name: name of event to track
        bookmark: Bookmark object
    """
    tracker.emit(
        event_name,
        {
            'course_id': unicode(bookmark.course_key),
            'bookmark_id': bookmark.resource_id,
            'component_type': bookmark.usage_key.block_type,
            'component_usage_id': unicode(bookmark.usage_key),
        }
    )
