from rest_framework import serializers
from rest_framework.utils.field_mapping import get_field_kwargs

from .fields import EnumChoiceField as ModelEnumChoiceField


class EnumChoiceField(serializers.Field):
    # TODO: `many` behaviour

    NO_KEY_MSG = 'Key {failing_key} is not a valid {enum_class_name}'

    default_error_messages = {
        'non_existent_key': NO_KEY_MSG
    }

    def __init__(self, enum_class, **kwargs):
        super().__init__(**kwargs)
        self.enum_class = enum_class

    def to_representation(self, value):
        return value.value

    def to_internal_value(self, value):
        # TODO: Handle extra arguments: `allow_null`, `required`, etc

        try:
            return self.enum_class(value)
        except ValueError:
            self.fail(
                'non_existent_key',
                failing_key=value,
                enum_class_name=self.enum_class.__name__
            )


class EnumChoiceModelSerializerMixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.serializer_field_mapping[ModelEnumChoiceField] = EnumChoiceField

    def build_standard_field(self, field_name, model_field):
        """
        By default `ModelSerializer.build_standard_field` coerces any field
        that has a model field with choices to `ChoiceField` wich returns the
        value directly.

        Since enum values resemble `EnumClass.ENUM_INSTANCE`
        they won't be able to be encoded by the JSONEncoder when being passed
        to a `Response`.
        """

        if isinstance(model_field, ModelEnumChoiceField):
            # These are kwargs, generated by `get_field_kwargs`
            # but are not needed for our field.
            # `model_field` is used only in children of DRF's `ModelField`
            # `choices` is not used because we use `field.enum_class` to validate the choice
            # `max_length` is generated from the model field's max_length and we don't use it
            dump_kwargs = ('model_field', 'choices', 'max_length')

            initial_kwargs = {
                'enum_class': model_field.enum_class,
                **get_field_kwargs(field_name, model_field)
            }
            finalized_kwargs = {
                key: value for key, value in initial_kwargs.items()
                if key not in dump_kwargs
            }

            return EnumChoiceField, finalized_kwargs

        return super().build_standard_field(field_name, model_field)
