from rest_framework import serializers


class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that allows for dynamic field selection, exclusion, and read-only fields.

    Methods:
        __init__(*args, **kwargs): Initializes the serializer and customizes fields based on 'fields' and 'exclude' arguments.
        unique_value_validator(field, value, error_message, look_up, check_parent_model, model, **kwargs):
            Custom validator for ensuring unique values in a model field.
    """

    def __init__(self, *args, **kwargs):
        """
        Initializes the serializer and customizes fields based on 'fields' and 'exclude' arguments.

        Args:
            *args: Positional arguments for the serializer.
            **kwargs: Keyword arguments for the serializer, including 'fields', 'exclude', and 'read_only_fields'.

        Returns:
            None
        """

        # Extract 'fields' and 'exclude' arguments from kwargs
        fields = kwargs.pop("fields", None)
        exclude = kwargs.pop("exclude", None)

        # Extract 'read_only_fields' from kwargs
        read_only_fields = kwargs.pop("read_only_fields", None)

        # Initialize the superclass (the parent class of DynamicFieldsModelSerializer)
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        for field in self.fields:
            self.fields[field].error_messages[
                "required"
            ] = "%s field is required." % field.capitalize().replace("_", " ")

        if fields is not None:
            # If 'fields' is specified, keep only the specified fields, remove the rest
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

        if exclude is not None:
            # If 'exclude' is specified, remove the specified fields from serialization
            existing = set(self.fields)
            for field_name in existing:
                if field_name in exclude:
                    self.fields.pop(field_name)

        if read_only_fields is not None:
            # Make specified fields read-only
            for f in read_only_fields:
                try:
                    self.fields[f].read_only = True
                except KeyError:
                    # not in fields anyway
                    pass

    def unique_value_validator(
            self,
            field,
            value,
            error_message,
            look_up,
            check_parent_model=False,
            model=None,
            **kwargs
    ):
        """
        A custom validator for ensuring unique values in a model field.

        Args:
            field: The field name to validate.
            value: The value to be validated for uniqueness.
            error_message: The error message to raise in case of non-uniqueness.
            look_up: The lookup parameter for filtering the model objects.
            check_parent_model: Whether to check the parent model for uniqueness.
            model: The model class to use for validation. Defaults to the serializer's model.
            kwargs: Additional keyword arguments for the filter.

        Returns:
            The validated value if it is unique.
        """
        error_message = serializers.ValidationError(error_message)
        instance = self.instance
        model = self.Meta.model if not model else model
        if check_parent_model:
            model = model._meta.get_parent_list()[0]

        kwargs[look_up] = value

        if instance:
            if (
                    getattr(instance, field) != value
                    and model.objects.filter(**kwargs).exists()
            ):
                raise error_message
        elif model.objects.filter(**kwargs).exists():
            raise error_message
        return value


class ReadOnlyModelSerializer(DynamicFieldsModelSerializer):
    """
    A ModelSerializer that makes all fields read-only for faster response.
    Extends DynamicFieldsModelSerializer for flexibility in specifying fields.

    Methods:
        get_fields(): Get the fields for serialization and set them as read-only.
    """

    def get_fields(self):
        """
        Get the fields for serialization and set them as read-only.

        Returns:
            dict: A dictionary of fields, all set to read-only.
        """
        fields = super().get_fields()
        for field in fields.values():
            # Set all fields to read-only, improving response speed
            field.read_only = True
        return fields


class DynamicFieldsSerializer(serializers.Serializer):
    """
    A serializer that allows custom error messages for required fields.

    Methods:
        __init__(*args, **kwargs): Initializes the serializer and sets custom error messages for required fields.
    """
    def __init__(self, *args, **kwargs):
        """
        Initializes the serializer and sets custom error messages for required fields.

        Args:
            *args: Positional arguments for the serializer.
            **kwargs: Keyword arguments for the serializer.

        Returns:
            None
        """
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].error_messages[
                "required"
            ] = "%s field is required." % field.capitalize().replace("_", " ")
