import json

from django import forms
from django.core import validators
from django.core.exceptions import ValidationError
from django.db.models import NOT_PROVIDED
from django.utils.encoding import smart_text

from idm.utils.i18n import get_lang_key


class ApiCharField(forms.CharField):
    null_allowed_by_default = True
    empty_values = list(validators.EMPTY_VALUES) + [NOT_PROVIDED]

    def __init__(self, max_length=None, min_length=None, *args, **kwargs):
        self.blank_allowed = kwargs.pop('blank_allowed', self.null_allowed_by_default)
        super(ApiCharField, self).__init__(max_length=max_length, min_length=min_length, *args, **kwargs)

    def to_python(self, value):
        if value in self.empty_values:
            if not self.required and self.blank_allowed:
                empty = ''
            else:
                empty = NOT_PROVIDED
            return empty
        return smart_text(value)


class ApiCharFieldDefaultNull(ApiCharField):
    null_allowed_by_default = False


class ApiChoiceField(forms.ChoiceField):
    def __init__(self, choices, **kwargs):
        kwargs.pop('widget', None)
        self.allow_custom_choice = kwargs.pop('custom', False)
        form_choices = [(choice['value'], choice['name'][get_lang_key()]) for choice in choices]
        super(ApiChoiceField, self).__init__(choices=form_choices, **kwargs)

    def valid_value(self, value):
        if self.allow_custom_choice:
            return True
        return super(ApiChoiceField, self).valid_value(value)


class ApiSuggestField(forms.CharField):
    _suggest_resources_registry = None

    @classmethod
    def get_fields_suggest_registry(cls):
        if cls._suggest_resources_registry is None:
            from idm.api.frontend.suggest.fields.usable_in_fields import _REGISTRY
            cls._suggest_resources_registry = _REGISTRY
        return cls._suggest_resources_registry

    def __init__(self, suggest, max_length=None, min_length=None, **kwargs):
        super(ApiSuggestField, self).__init__(**kwargs)
        registry = self.get_fields_suggest_registry()
        self.suggest_resource = registry[suggest]

    def validate(self, value):
        super(ApiSuggestField, self).validate(value)
        suggested_objects = self.suggest_resource.get_objects_by_id(value)
        assert len(suggested_objects) <= 1, 'There can be no multiple objects with the same id in suggest'
        if not suggested_objects:
            raise forms.ValidationError(
                'Object with id={} is not a valid object from suggest "{}"'.format(
                    value,
                    self.suggest_resource.Meta.suggest_name
                )
            )


class JsonValidator:

    def __init__(self, type):
        self.type = type

    def __call__(self, value):
        try:
            value = json.loads(value)
        except json.JSONDecodeError:
            raise ValidationError('invalid_json')
        else:
            if not isinstance(value, self.type):
                raise ValidationError('invalid_json_type')


class JsonDictField(forms.CharField):

    default_validators = [JsonValidator(dict)]
