"""Fields useful for edX API implementations."""
from django.core.exceptions import ValidationError

from rest_framework.serializers import CharField, Field


class ExpandableField(Field):
    """Field that can dynamically use a more detailed serializer based on a user-provided "expand" parameter.

    Kwargs:
      collapsed_serializer (Serializer): the serializer to use for a non-expanded representation.
      expanded_serializer (Serializer): the serializer to use for an expanded representation.
      exclude_expand_fields (set(str)): a set of fields which will not be expanded by sub-serializers.
    """
    def __init__(self, **kwargs):
        """Sets up the ExpandableField with the collapsed and expanded versions of the serializer."""
        assert 'collapsed_serializer' in kwargs and 'expanded_serializer' in kwargs
        self.collapsed = kwargs.pop('collapsed_serializer')
        self.expanded = kwargs.pop('expanded_serializer')
        self.exclude_expand_fields = kwargs.pop('exclude_expand_fields', set())
        super(ExpandableField, self).__init__(**kwargs)

    def field_to_native(self, obj, field_name):
        """Converts obj to a native representation, using the expanded serializer if the context requires it."""
        if 'expand' in self.context and field_name in self.context['expand']:
            self.expanded.initialize(self, field_name)
            self.expanded.context['expand'] = list(set(self.expanded.context['expand']) - self.exclude_expand_fields)
            return self.expanded.field_to_native(obj, field_name)
        else:
            self.collapsed.initialize(self, field_name)
            return self.collapsed.field_to_native(obj, field_name)


class NonEmptyCharField(CharField):
    """
    A field that enforces non-emptiness even for partial updates.

    This is necessary because prior to version 3, DRF skips validation for empty
    values. Thus, CharField's min_length and RegexField cannot be used to
    enforce this constraint.
    """
    def validate(self, value):
        super(NonEmptyCharField, self).validate(value)
        if not value.strip():
            raise ValidationError(self.error_messages["required"])
