from staff.django_phonenumbers import PhoneField
from json import dumps

from django.forms import (
    BooleanField,
    CharField,
    ChoiceField,
    ComboField,
    DateField,
    DateTimeField,
    DecimalField,
    EmailField,
    Field,
    FileField,
    FilePathField,
    FloatField,
    GenericIPAddressField,
    ImageField,
    IntegerField,
    MultiValueField,
    MultipleChoiceField,
    NullBooleanField,
    RegexField,
    SlugField,
    SplitDateTimeField,
    TimeField,
    TypedChoiceField,
    TypedMultipleChoiceField,
    URLField,
)
from django.forms.models import ModelChoiceField

from staff.lib.utils.qs_values import localize


class StaffField(Field):

    default_error_messages = {
        # This field is required.
        'required': dumps({'error_key': 'default-field-required'}),
        # Enter a valid value.
        'invalid': dumps({'error_key': 'default-field-invalid'}),
        # Ensure this value is %(limit_value)s (it is %(show_value)s).
        'limit_value':  dumps({
            'error_key': 'default-field-limit_value',
            'params': {
                'limit_value': '%(limit_value)s',
                'show_value': '%(show_value)s'
            }
        }),
        # Ensure this value is less than or equal to %(limit_value)s.
        'max_value': dumps({
            'error_key': 'default-field-max_value',
            'params': {'limit_value': '%(limit_value)s'}
        }),
        # Ensure this value is greater than or equal to %(limit_value)s.
        'min_value': dumps({
            'error_key': 'default-field-min_value',
            'params': {'limit_value': '%(limit_value)s'}
        }),
        # Ensure this value has at least %(limit_value)d characters
        # (it has %(show_value)d).
        'min_length': dumps({
            'error_key': 'default-field-min_length',
            'params': {
                'limit_value': '%(limit_value)s',
                'show_value': '%(show_value)s'
            }
        }),
        # Ensure this value has at most %(limit_value)d characters
        # (it has %(show_value)d).
        'max_length': dumps({
            'error_key': 'default-field-max_length',
            'params': {
                'limit_value': '%(limit_value)s',
                'show_value': '%(show_value)s'
            }
        }),
    }

    def __init__(self, *args, **kwargs):
        self.front_params = kwargs.pop('front_params', {})
        super(StaffField, self).__init__(*args, **kwargs)


class StaffCharField(CharField, StaffField):
    pass


class StaffIntegerField(IntegerField, StaffField):
    default_error_messages = {
        # Enter a whole number.
        'invalid': dumps({'error_key': 'integer-field-invalid'}),
    }


class StaffDateField(DateField, StaffField):
    default_error_messages = {
        # Enter a valid date.
        'invalid':  dumps({'error_key': 'date-field-invalid'}),
        'required': dumps({'error_key': 'date-field-required'}),
    }


class StaffTimeField(TimeField, StaffField):
    default_error_messages = {
        # Enter a valid time.
        'invalid':  dumps({'error_key': 'time-field-invalid'}),
    }


class StaffDateTimeField(DateTimeField, StaffField):
    default_error_messages = {
        # Enter a valid date/time.
        'invalid':  dumps({'error_key': 'datetime-field-invalid'}),
    }


class StaffRegexField(RegexField, StaffCharField):
    pass


class StaffEmailField(EmailField, StaffCharField):
    pass


class StaffFileField(FileField, StaffField):
    default_error_messages = {
        # No file was submitted. Check the encoding type on the form.
        'invalid':  dumps({'error_key': 'file-field-invalid'}),
        # No file was submitted.
        'missing':  dumps({'error_key': 'file-field-missing'}),
        # The submitted file is empty.
        'empty':  dumps({'error_key': 'file-field-empty'}),
        # Ensure this filename has at most %(max)d characters
        # (it has %(length)d).
        'max_length':  dumps({
            'error_key': 'file-field-max_length',
            'params': {
                'max': '%(max)s',
                'length': '%(length)s'
            }
        }),
        # Please either submit a file or check the clear checkbox, not both.
        'contradiction': dumps({'error_key': 'file-field-contradiction'}),
    }


class StaffImageField(ImageField, StaffFileField):
    default_error_messages = {
        # Upload a valid image. The file you uploaded was either not an image
        # or a corrupted image.
        'invalid_image':  dumps({'error_key': 'image-field-invalid_image'}),
    }


class StaffURLField(URLField, StaffCharField):
    default_error_messages = {
        # Enter a valid URL.
        'invalid':  dumps({'error_key': 'url-field-invalid'}),
        # This URL appears to be a broken link.
        'invalid_link':  dumps({'error_key': 'url-field-invalid_link'}),
    }


class StaffBooleanField(BooleanField, StaffField):
    pass


class StaffNullBooleanField(NullBooleanField, StaffBooleanField):
    pass


class StaffChoiceField(ChoiceField, StaffField):
    default_error_messages = {
        # Select a valid choice. %(value)s is not one of the available choices.
        'invalid_choice':  dumps({
            'error_key': 'choice-field-invalid_choice',
            'params': {'value': '%(value)s'}
        }),
    }
    choices_source = None

    def __init__(
            self, choices=(), empty_label=None, required=True, widget=None,
            label=None, initial=None, help_text=None, choices_source=None,
            *args, **kwargs
    ):
        if choices_source is not None:
            self.choices_source = choices_source
            choices = self.get_staff_choices()

        if not required and empty_label is not None:
            choices = tuple([('', empty_label)] + list(choices))

        super(StaffChoiceField, self).__init__(
            choices=choices, required=required, widget=widget, label=label,
            initial=initial, help_text=help_text, *args, **kwargs)

    def get_staff_choices(self):
        # Удалится после добавления choices() с ключами в Choices DIS, а пока так (c) another
        return [(db_value, self.choices_source.get_name(db_value))
                for db_value, key_value in self.choices_source.choices()]


class StaffMultipleChoiceField(MultipleChoiceField, StaffChoiceField):
    default_error_messages = {
        # Select a valid choice. %(value)s is not one of the available choices.
        'invalid_choice':  dumps({
            'error_key': 'multiplechoice-field-invalid_choice',
            'params': {'value': '%(value)s'}}),
        # Enter a list of values.
        'invalid_list':  dumps({
            'error_key': 'multiplechoice-field-invalid_list'
        }),
    }


class StaffComboField(ComboField, StaffField):
    pass


class StaffMultiValueField(MultiValueField, StaffField):
    default_error_messages = {
        # Enter a list of values.
        'invalid':  dumps({'error_key': 'multivalue-field-invalid'}),
    }


class StaffFloatField(FloatField, StaffIntegerField):
    default_error_messages = {
        # Enter a number.
        'invalid':  dumps({'error_key': 'float-field-invalid'}),
    }


class StaffDecimalField(DecimalField, StaffIntegerField):
    default_error_messages = {
        # Enter a number.
        'invalid':  dumps({'error_key': 'decimal-field-invalid'}),
        # Ensure this value is less than or equal to %(limit_value)s.
        'max_value':  dumps({
            'error_key': 'decimal-field-max_value',
            'params': {'limit_value': '%(limit_value)s'}
        }),
        # Ensure this value is greater than or equal to %(limit_value)s.
        'min_value': dumps({
            'error_key': 'decimal-field-min_value',
            'params': {'limit_value': '%(limit_value)s'}
        }),
        # Ensure that there are no more than %(max)s digits in total.
        'max_digits':  dumps({
            'error_key': 'decimal-field-max_digits',
            'params': {'max': '%(max)s'}
        }),
        # Ensure that there are no more than %(max)s decimal places.
        'max_decimal_places': dumps({
            'error_key': 'decimal-field-max_decimal_places',
            'params': {'max': '%(max)s'}
        }),
        # Ensure that there are no more than %(max)s digits before the decimal
        # point.
        'max_whole_digits': dumps({
            'error_key': 'decimal-field-max_whole_digits',
            'params': {'max': '%(max)s'}
        }),
    }


class StaffSplitDateTimeField(SplitDateTimeField, StaffMultiValueField):
    default_error_messages = {
        # Enter a valid date.
        'invalid_date':  dumps({
            'error_key': 'splitdatetime-field-invalid_date'
        }),
        # Enter a valid time.
        'invalid_time':  dumps({
            'error_key': 'splitdatetime-field-invalid_time'
        }),
    }


class StaffIPAddressField(GenericIPAddressField, StaffCharField):
    pass


class StaffGenericIPAddressField(GenericIPAddressField, StaffCharField):
    pass


class StaffFilePathField(FilePathField, StaffChoiceField):
    pass


class StaffSlugField(SlugField, StaffCharField):
    default_error_messages = {
        # Enter a valid 'slug' consisting of letters, numbers, underscores
        # or hyphens.
        'invalid':  dumps({
            'error_key': 'slug-field-invalid'
        }),
    }


class StaffTypedChoiceField(TypedChoiceField, StaffChoiceField):
    pass


class StaffTypedMultipleChoiceField(TypedMultipleChoiceField, StaffMultipleChoiceField):
    pass


class StaffModelChoiceField(ModelChoiceField, StaffChoiceField):
    default_error_messages = {
        # Select a valid choice. That choice is not one of the available choices
        'invalid_choice':  dumps({
            'error_key': 'modelchoice-field-invalid_choice'
        })
    }

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


class StaffSuggestField(StaffIntegerField):
    default_error_messages = {
        'invalid': dumps({'error_key': 'person-does-not-exist'})
    }


class StaffPersonSuggestField(StaffModelChoiceField):
    pass


class StaffLocalizedModelChoiceField(StaffModelChoiceField):

    def __init__(self, *args, **kwargs):
        self.label_name = kwargs.pop('label_name', 'name')
        super(StaffLocalizedModelChoiceField, self).__init__(*args, **kwargs)

    def label_from_instance(self, obj):
        ln = self.label_name
        return localize(
            {
                ln: getattr(obj, ln),
                ln + '_en': getattr(obj, ln + '_en'),
            }
        )[ln]


class StaffPhoneField(PhoneField, StaffField):
    pass
