import copy

from django.db import models
from django.forms import widgets
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from rest_framework.utils import model_meta

from ..core.validators import validate_password


class SetPasswordSerializer(serializers.Serializer):
    password = serializers.CharField(max_length=128, label=_('Password'),
        style={'widget': widgets.PasswordInput}, validators=[validate_password])


class HyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
    """ support for postonly_fields, fields whose value can only be set on post """
    
    def validate(self, attrs):
        """ calls model.clean() """
        attrs = super(HyperlinkedModelSerializer, self).validate(attrs)
        if isinstance(attrs, models.Model):
            return attrs
        validated_data = dict(attrs)
        ModelClass = self.Meta.model
        # Remove many-to-many relationships from validated_data.
        info = model_meta.get_field_info(ModelClass)
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                validated_data.pop(field_name)
        if self.instance:
            # on update: Merge provided fields with instance field
            instance = copy.deepcopy(self.instance)
            for key, value in validated_data.items():
                setattr(instance, key, value)
        else:
            instance = ModelClass(**validated_data)
        instance.clean()
        return attrs
    
    def post_only_cleanning(self, instance, validated_data):
        """ removes postonly_fields from attrs """
        model_attrs = dict(**validated_data)
        post_only_fields = getattr(self, 'post_only_fields', None)
        if instance is not None and post_only_fields:
            for attr, value in validated_data.items():
                if attr in post_only_fields:
                    model_attrs.pop(attr)
        return model_attrs
    
    def update(self, instance, validated_data):
        """ removes postonly_fields from attrs when not posting """
        model_attrs = self.post_only_cleanning(instance, validated_data)
        return super(HyperlinkedModelSerializer, self).update(instance, model_attrs)
    
    def partial_update(self, instance, validated_data):
        """ removes postonly_fields from attrs when not posting """
        model_attrs = self.post_only_cleanning(instance, validated_data)
        return super(HyperlinkedModelSerializer, self).partial_update(instance, model_attrs)


class RelatedHyperlinkedModelSerializer(HyperlinkedModelSerializer):
    """ returns object on to_internal_value based on URL """
    def to_internal_value(self, data):
        url = data.get('url')
        if not url:
            raise ValidationError({
                'url': "URL is required."
            })
        account = self.get_account()
        queryset = self.Meta.model.objects.filter(account=self.get_account())
        self.fields['url'].queryset = queryset
        obj = self.fields['url'].to_internal_value(url)
        return obj


class SetPasswordHyperlinkedSerializer(HyperlinkedModelSerializer):
    password = serializers.CharField(max_length=128, label=_('Password'),
        validators=[validate_password], write_only=True, required=False,
        style={'widget': widgets.PasswordInput})
    
    def validate_password(self, attrs, source):
        """ POST only password """
        if self.instance:
            if 'password' in attrs:
                raise serializers.ValidationError(_("Can not set password"))
        elif 'password' not in attrs:
            raise serializers.ValidationError(_("Password required"))
        return attrs
    
    def validate(self, attrs):
        """ remove password in case is not a real model field """
        try:
            self.Meta.model._meta.get_field_by_name('password')
        except models.FieldDoesNotExist:
            pass
        else:
            password = attrs.pop('password', None)
        attrs = super(SetPasswordSerializer, self).validate()
        if password is not None:
            attrs['password'] = password
        return attrs
    
    def create(self, validated_data):
        password = validated_data.pop('password')
        instance = self.Meta.model(**validated_data)
        instance.set_password(password)
        instance.save()
        return instance


#class MultiSelectField(serializers.ChoiceField):
#    widget = widgets.CheckboxSelectMultiple
#    
#    def field_from_native(self, data, files, field_name, into):
#        """ convert multiselect data into comma separated string """
#        if field_name in data:
#            data = data.copy()
#            try:
#                # data is a querydict when using forms
#                data[field_name] = ','.join(data.getlist(field_name))
#            except AttributeError:
#                data[field_name] = ','.join(data[field_name])
#        return super(MultiSelectField, self).field_from_native(data, files, field_name, into)
#    
#    def valid_value(self, value):
#        """ checks for each item if is a valid value """
#        for val in value.split(','):
#            valid = super(MultiSelectField, self).valid_value(val)
#            if not valid:
#                return False
#        return True