"""
Course API forms
"""

from collections import namedtuple

from django.core.exceptions import ValidationError
from django.forms import CharField, Form
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey

from openedx.core.djangoapps.util.forms import ExtendedNullBooleanField


class UsernameValidatorMixin(object):
    """
    Mixin class for validating the username parameter.
    """

    def clean_username(self):
        """
        Ensures the username is provided unless the request is made
        as an anonymous user.
        """
        username = self.cleaned_data.get('username')
        return username or ''


class CourseDetailGetForm(UsernameValidatorMixin, Form):
    """
    A form to validate query parameters in the course detail endpoint
    """
    username = CharField(required=False)
    course_key = CharField(required=True)

    def clean_course_key(self):
        """
        Ensure a valid `course_key` was provided.
        """
        course_key_string = self.cleaned_data['course_key']
        try:
            return CourseKey.from_string(course_key_string)
        except InvalidKeyError:
            raise ValidationError("'{}' is not a valid course key.".format(unicode(course_key_string)))


class CourseListGetForm(UsernameValidatorMixin, Form):
    """
    A form to validate query parameters in the course list retrieval endpoint
    """
    username = CharField(required=False)
    org = CharField(required=False)

    # white list of all supported filter fields
    filter_type = namedtuple('filter_type', ['param_name', 'field_name'])
    supported_filters = [
        filter_type(param_name='mobile', field_name='mobile_available'),
    ]
    mobile = ExtendedNullBooleanField(required=False)

    def clean(self):
        """
        Return cleaned data, including additional filters.
        """
        cleaned_data = super(CourseListGetForm, self).clean()

        # create a filter for all supported filter fields
        filter_ = dict()
        for supported_filter in self.supported_filters:
            if cleaned_data.get(supported_filter.param_name) is not None:
                filter_[supported_filter.field_name] = cleaned_data[supported_filter.param_name]
        cleaned_data['filter_'] = filter_ or None

        return cleaned_data
