"""
API views for badges
"""
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework import generics
from rest_framework.exceptions import APIException

from openedx.core.djangoapps.user_api.permissions import is_field_shared_factory
from openedx.core.lib.api.authentication import (
    OAuth2AuthenticationAllowInactiveUser,
    SessionAuthenticationAllowInactiveUser
)
from openedx.core.djangoapps.xmodule_django.models import CourseKeyField

from badges.models import BadgeAssertion
from .serializers import BadgeAssertionSerializer


class InvalidCourseKeyError(APIException):
    """
    Raised the course key given isn't valid.
    """
    status_code = 400
    default_detail = "The course key provided was invalid."


class UserBadgeAssertions(generics.ListAPIView):
    """
    ** Use cases **

        Request a list of assertions for a user, optionally constrained to a course.

    ** Example Requests **

        GET /api/badges/v1/assertions/user/{username}/

    ** Response Values **

        Body comprised of a list of objects with the following fields:

        * badge_class: The badge class the assertion was awarded for. Represented as an object
          with the following fields:
            * slug: The identifier for the badge class
            * issuing_component: The software component responsible for issuing this badge.
            * display_name: The display name of the badge.
            * course_id: The course key of the course this badge is scoped to, or null if it isn't scoped to a course.
            * description: A description of the award and its significance.
            * criteria: A description of what is needed to obtain this award.
            * image_url: A URL to the icon image used to represent this award.
        * image_url: The baked assertion image derived from the badge_class icon-- contains metadata about the award
          in its headers.
        * assertion_url: The URL to the OpenBadges BadgeAssertion object, for verification by compatible tools
          and software.

    ** Params **

        * slug (optional): The identifier for a particular badge class to filter by.
        * issuing_component (optional): The issuing component for a particular badge class to filter by
          (requires slug to have been specified, or this will be ignored.) If slug is provided and this is not,
          assumes the issuing_component should be empty.
        * course_id (optional): Returns assertions that were awarded as part of a particular course. If slug is
          provided, and this field is not specified, assumes that the target badge has an empty course_id field.
          '*' may be used to get all badges with the specified slug, issuing_component combination across all courses.

    ** Returns **

        * 200 on success, with a list of Badge Assertion objects.
        * 403 if a user who does not have permission to masquerade as
          another user specifies a username other than their own.
        * 404 if the specified user does not exist

        {
            "count": 7,
            "previous": null,
            "num_pages": 1,
            "results": [
                {
                    "badge_class": {
                        "slug": "special_award",
                        "issuing_component": "openedx__course",
                        "display_name": "Very Special Award",
                        "course_id": "course-v1:edX+DemoX+Demo_Course",
                        "description": "Awarded for people who did something incredibly special",
                        "criteria": "Do something incredibly special.",
                        "image": "http://example.com/media/badge_classes/badges/special_xdpqpBv_9FYOZwN.png"
                    },
                    "image_url": "http://badges.example.com/media/issued/cd75b69fc1c979fcc1697c8403da2bdf.png",
                    "assertion_url": "http://badges.example.com/public/assertions/07020647-e772-44dd-98b7-d13d34335ca6"
                },
            ...
            ]
        }
    """
    serializer_class = BadgeAssertionSerializer
    authentication_classes = (
        OAuth2AuthenticationAllowInactiveUser,
        SessionAuthenticationAllowInactiveUser
    )
    permission_classes = (is_field_shared_factory("accomplishments_shared"),)

    def filter_queryset(self, queryset):
        """
        Return most recent to least recent badge.
        """
        return queryset.order_by('-created')

    def get_queryset(self):
        """
        Get all badges for the username specified.
        """
        queryset = BadgeAssertion.objects.filter(user__username=self.kwargs['username'])
        provided_course_id = self.request.query_params.get('course_id')
        if provided_course_id == '*':
            # We might want to get all the matching course scoped badges to see how many courses
            # a user managed to get a specific award on.
            course_id = None
        elif provided_course_id:
            try:
                course_id = CourseKey.from_string(provided_course_id)
            except InvalidKeyError:
                raise InvalidCourseKeyError
        elif 'slug' not in self.request.query_params:
            # Need to get all badges for the user.
            course_id = None
        else:
            # Django won't let us use 'None' for querying a ForeignKey field. We have to use this special
            # 'Empty' value to indicate we're looking only for badges without a course key set.
            course_id = CourseKeyField.Empty

        if course_id is not None:
            queryset = queryset.filter(badge_class__course_id=course_id)
        if self.request.query_params.get('slug'):
            queryset = queryset.filter(
                badge_class__slug=self.request.query_params['slug'],
                badge_class__issuing_component=self.request.query_params.get('issuing_component', '')
            )
        return queryset
