from django.apps import apps
from django.core import exceptions

from hashids import Hashids
from rest_framework import fields, serializers

from hashid_field.conf import settings
from hashid_field.hashid import Hashid


class UnconfiguredHashidSerialField(fields.Field):
    def bind(self, field_name, parent):
        super().bind(field_name, parent)
        raise exceptions.ImproperlyConfigured(
            "The field '{field_name}' on {parent} must be explicitly declared when used with a ModelSerializer".format(
                field_name=field_name, parent=parent.__class__.__name__))


class HashidSerializerMixin(object):
    usage_text = "Must pass a HashidField, HashidAutoField or 'app_label.model.field'"

    def __init__(self, **kwargs):
        self.hashid_salt = kwargs.pop('salt', settings.HASHID_FIELD_SALT)
        self.hashid_min_length = kwargs.pop('min_length', 7)
        self.hashid_alphabet = kwargs.pop('alphabet', Hashids.ALPHABET)

        source_field = kwargs.pop('source_field', None)
        if source_field:
            from hashid_field import HashidField, HashidAutoField
            if isinstance(source_field, str):
                try:
                    app_label, model_name, field_name = source_field.split(".")
                except ValueError:
                    raise ValueError(self.usage_text)
                model = apps.get_model(app_label, model_name)
                source_field = model._meta.get_field(field_name)
            elif not isinstance(source_field, (HashidField, HashidAutoField)):
                raise TypeError(self.usage_text)
            self.hashid_salt, self.hashid_min_length, self.hashid_alphabet = \
                source_field.salt, source_field.min_length, source_field.alphabet
        self._hashids = Hashids(salt=self.hashid_salt, min_length=self.hashid_min_length, alphabet=self.hashid_alphabet)
        super().__init__(**kwargs)

    def to_internal_value(self, data):
        try:
            value = super().to_internal_value(data)
            return Hashid(value, hashids=self._hashids)
        except ValueError:
            raise serializers.ValidationError("Invalid int or Hashid string")


class HashidSerializerCharField(HashidSerializerMixin, fields.CharField):
    def to_representation(self, value):
        return value.hashid


class HashidSerializerIntegerField(HashidSerializerMixin, fields.IntegerField):
    def to_representation(self, value):
        return value.id