"""Resolwe relation serializer."""
from django.db import transaction

from rest_framework import serializers

from resolwe.flow.models.collection import Collection
from resolwe.flow.models.entity import Relation, RelationPartition, RelationType
from resolwe.rest.serializers import SelectiveFieldMixin

from .base import ResolweBaseSerializer


class RelationPartitionSerializer(SelectiveFieldMixin, serializers.ModelSerializer):
    """Serializer for RelationPartition objects."""

    class Meta:
        """RelationPartitionSerializer Meta options."""

        model = RelationPartition
        fields = ('id', 'entity', 'position', 'label')


class RelationSerializer(ResolweBaseSerializer):
    """Serializer for Relation objects."""

    partitions = RelationPartitionSerializer(source='relationpartition_set', many=True)
    collection = serializers.PrimaryKeyRelatedField(queryset=Collection.objects.all())
    type = serializers.SlugRelatedField(queryset=RelationType.objects.all(), slug_field='name')

    class Meta:
        """RelationSerializer Meta options."""

        model = Relation
        read_only_fields = (
            'created',
            'id',
            'modified',
        )
        update_protected_fields = (
            'contributor',
            'type',
        )
        fields = read_only_fields + update_protected_fields + (
            'collection',
            'category',
            'partitions',
            'unit',
        )

    def validate_partitions(self, partitions):
        """Raise validation error if list of partitions is empty."""
        if not partitions:
            raise serializers.ValidationError("List of partitions must not be empty.")

        return partitions

    def _create_partitions(self, instance, partitions):
        """Create partitions."""
        for partition in partitions:
            RelationPartition.objects.create(
                relation=instance,
                entity=partition['entity'],
                label=partition.get('label', None),
                position=partition.get('position', None),
            )

    def create(self, validated_data):
        """Create ``Relation`` object and add partitions of ``Entities``."""
        # `partitions` field is renamed to `relationpartition_set` based on source of nested serializer
        partitions = validated_data.pop('relationpartition_set')

        with transaction.atomic():
            instance = Relation.objects.create(**validated_data)
            self._create_partitions(instance, partitions)

        return instance

    def update(self, instance, validated_data):
        """Update ``Relation``."""
        # `partitions` field is renamed to `relationpartition_set` based on source of nested serializer
        partitions = validated_data.pop('relationpartition_set', None)

        with transaction.atomic():
            instance = super().update(instance, validated_data)

            if partitions is not None:
                # TODO: Apply the diff instead of recreating all objects.
                instance.relationpartition_set.all().delete()
                self._create_partitions(instance, partitions)

        return instance
