"""
The Enrollment API Views should be simple, lean HTTP endpoints for API access. This should
consist primarily of authentication, request validation, and serialization.

"""
from rest_framework import status
from rest_framework.authentication import OAuth2Authentication
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
from enrollment import api
from enrollment.errors import CourseNotFoundError, CourseEnrollmentError, CourseModeNotFoundError
from util.authentication import SessionAuthenticationAllowInactiveUser


class EnrollmentUserThrottle(UserRateThrottle):
    """Limit the number of requests users can make to the enrollment API."""
    # TODO Limit significantly after performance testing.  # pylint: disable=fixme
    rate = '50/second'


class EnrollmentView(APIView):
    """ Enrollment API View for creating, updating, and viewing course enrollments. """

    authentication_classes = OAuth2Authentication, SessionAuthenticationAllowInactiveUser
    permission_classes = permissions.IsAuthenticated,
    throttle_classes = EnrollmentUserThrottle,

    def get(self, request, course_id=None, user=None):
        """Create, read, or update enrollment information for a user.

        HTTP Endpoint for all CRUD operations for a user course enrollment. Allows creation, reading, and
        updates of the current enrollment for a particular course.

        Args:
            request (Request): To get current course enrollment information, a GET request will return
                information for the current user and the specified course.
            course_id (str): URI element specifying the course location. Enrollment information will be
                returned, created, or updated for this particular course.
            user (str): The user username associated with this enrollment request.

        Return:
            A JSON serialized representation of the course enrollment.

        """
        if request.user.username != user:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)
        try:
            return Response(api.get_enrollment(user, course_id))
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while retrieving enrollments for user "
                        u"'{user}' in course '{course_id}'"
                    ).format(user=user, course_id=course_id)
                }
            )


class EnrollmentCourseDetailView(APIView):
    """ Enrollment API View for viewing course enrollment details. """

    authentication_classes = []
    permission_classes = []
    throttle_classes = EnrollmentUserThrottle,

    def get(self, request, course_id=None):
        """Read enrollment information for a particular course.

        HTTP Endpoint for retrieving course level enrollment information.

        Args:
            request (Request): To get current course enrollment information, a GET request will return
                information for the specified course.
            course_id (str): URI element specifying the course location. Enrollment information will be
                returned.

        Return:
            A JSON serialized representation of the course enrollment details.

        """
        try:
            return Response(api.get_course_enrollment_details(course_id))
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"No course found for course ID '{course_id}'"
                    ).format(course_id=course_id)
                }
            )


class EnrollmentListView(APIView):
    """ Enrollment API List View for viewing all course enrollments for a user. """

    authentication_classes = OAuth2Authentication, SessionAuthenticationAllowInactiveUser
    permission_classes = permissions.IsAuthenticated,
    throttle_classes = EnrollmentUserThrottle,

    def get(self, request):
        """List out all the enrollments for the current user

        Returns a JSON response with all the course enrollments for the current user.

        Args:
            request (Request): The GET request for course enrollment listings.
            user (str): Get all enrollments for the specified user's username.

        Returns:
            A JSON serialized representation of the user's course enrollments.

        """
        user = request.GET.get('user', request.user.username)
        if request.user.username != user:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)
        try:
            return Response(api.get_enrollments(user))
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while retrieving enrollments for user '{user}'"
                    ).format(user=user)
                }
            )

    def post(self, request):
        """Create a new enrollment

        Creates a new enrollment based on the data posted. Currently all that can be specified is
        the course id.  All other attributes will be determined by the server, and cannot be updated
        through this endpoint.

        By default, this will attempt to create the enrollment with course mode 'honor'. If the course
        does not have an 'honor' course mode, it will fail as a bad request and list the available
        course modes.

        Args:
            request (Request): The POST request to create a new enrollment. POST DATA should contain
                'course_details' with an attribute 'course_id' to identify which course to enroll in.
                'user' may be specified as well, but must match the username of the authenticated user.
                Ex. {'user': 'Bob', 'course_details': { 'course_id': 'edx/demo/T2014' } }

        Returns:
            A JSON serialized representation of the user's new course enrollment.

        """
        user = request.DATA.get('user', request.user.username)
        if not user:
            user = request.user.username
        if user != request.user.username:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if 'course_details' not in request.DATA or 'course_id' not in request.DATA['course_details']:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )
        course_id = request.DATA['course_details']['course_id']

        try:
            return Response(api.add_enrollment(user, course_id))
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The course mode '{mode}' is not available for course '{course_id}'."
                    ).format(mode="honor", course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{user}' in course '{course_id}'"
                    ).format(user=user, course_id=course_id)
                }
            )