import binascii, hashlib
from pydash import _
from inflection import camelize
from rest_framework.exceptions import ValidationError
from dynamic_rest.serializers import DynamicModelSerializer, DynamicRelationField
from alto_django_utils.utils import lower_first

class UpperCamelMixin:
  def to_representation(self, instance):
    repre = super().to_representation(instance)

    return { camelize(name): value for name, value in repre.items() }

class LowerCamelMixin:
  def to_representation(self, instance):
    repre = super().to_representation(instance)

    return { lower_first(camelize(name)): value for name, value in repre.items() }

class RemoveNullsMixin:
  def to_representation(self, instance):
    repre = super().to_representation(instance)

    return { key: val for key, val in repre.items() if val != None }


def validate(field_name, value, field):
  try:
    field.run_validation(value)
  except ValidationError as e:
    raise ValidationError({ field_name: e.detail })

def parse_field_value(serializer_class, field_name, value):
  serializer = serializer_class(data = { field_name: value }, partial = True)
  serializer.is_valid(raise_exception = True)

  return serializer.validated_data[field_name]

def create(serializer_class, data):
  serializer = serializer_class(data = data)
  serializer.is_valid(raise_exception = True)
  serializer.save()

  return serializer


# used now for danube Employees (in datastore)
def encode_password(password):
  return str(binascii.hexlify(
    hashlib.pbkdf2_hmac('sha256', bytes(password, 'ascii'), b'salt', 100000)), 'ascii')


def serializer_name_for(model):
  return model._meta.app_label + '.serializers.' + model.__name__ + 'Serializer'

def belong_to_fields_for(model):
  model_fields = filter(lambda field: field.many_to_one or field.one_to_one,
                        model._meta.get_fields())

  return { field.name: DynamicRelationField(serializer_name_for(field.related_model))
           for field in model_fields }

def has_many_fields_for(model):
  model_fields = filter(lambda field: field.one_to_many, model._meta.get_fields())

  return { field.name: DynamicRelationField(serializer_name_for(field.related_model),
                                            many = True)
           for field in model_fields }

def serializer_for(model, fields = '__all__', *validators):
  meta_attrs = { 'model': model, 'fields': fields }

  if validators: meta_attrs['validators'] = validators

  attributes = { 'Meta': type('Meta', (object,), meta_attrs) }

  belong_to = belong_to_fields_for(model)
  has_many  = has_many_fields_for(model)

  if fields != '__all__':
    belong_to = _.pick(belong_to, *fields)
    has_many  = _.pick(has_many,  *fields)

  attributes.update(belong_to)
  attributes.update(has_many)

  return type(model.__name__ + 'Serializer', (DynamicModelSerializer,), attributes)
