from django.db import transaction
from django.utils.translation import ugettext as _

from openslides.core.config import config
from openslides.utils.rest_api import (
    CharField,
    IntegerField,
    ModelSerializer,
    PrimaryKeyRelatedField,
    ValidationError,
)

from .models import (
    Category,
    Motion,
    MotionLog,
    MotionOption,
    MotionPoll,
    MotionVersion,
    MotionVote,
    State,
    Workflow,
)


def validate_workflow_field(value):
    """
    Validator to ensure that the workflow with the given id exists.
    """
    if not Workflow.objects.filter(pk=value).exists():
        raise ValidationError(_('Workflow %(pk)d does not exist.') % {'pk': value})


class CategorySerializer(ModelSerializer):
    """
    Serializer for motion.models.Category objects.
    """
    class Meta:
        model = Category
        fields = ('id', 'name', 'prefix',)


class StateSerializer(ModelSerializer):
    """
    Serializer for motion.models.State objects.
    """
    class Meta:
        model = State
        fields = (
            'id',
            'name',
            'action_word',
            'icon',
            'required_permission_to_see',
            'allow_support',
            'allow_create_poll',
            'allow_submitter_edit',
            'versioning',
            'leave_old_version_active',
            'dont_set_identifier',
            'next_states',)


class WorkflowSerializer(ModelSerializer):
    """
    Serializer for motion.models.Workflow objects.
    """
    state_set = StateSerializer(many=True, read_only=True)
    first_state = PrimaryKeyRelatedField(read_only=True)

    class Meta:
        model = Workflow
        fields = ('id', 'name', 'state_set', 'first_state',)


class MotionLogSerializer(ModelSerializer):
    """
    Serializer for motion.models.MotionLog objects.
    """
    class Meta:
        model = MotionLog
        fields = ('message_list', 'person', 'time',)


class MotionVoteSerializer(ModelSerializer):
    """
    Serializer for motion.models.MotionVote objects.
    """
    class Meta:
        model = MotionVote
        fields = ('value', 'weight',)


class MotionOptionSerializer(ModelSerializer):
    """
    Serializer for motion.models.MotionOption objects.
    """
    motionvote_set = MotionVoteSerializer(many=True, read_only=True)

    class Meta:
        model = MotionOption
        fields = ('motionvote_set',)


class MotionPollSerializer(ModelSerializer):
    """
    Serializer for motion.models.MotionPoll objects.
    """
    motionoption_set = MotionOptionSerializer(many=True, read_only=True)

    class Meta:
        model = MotionPoll
        fields = (
            'poll_number',
            'motionoption_set',
            'votesvalid',
            'votesinvalid',
            'votescast',)


class MotionVersionSerializer(ModelSerializer):
    """
    Serializer for motion.models.MotionVersion objects.
    """
    class Meta:
        model = MotionVersion
        fields = (
            'id',
            'version_number',
            'creation_time',
            'title',
            'text',
            'reason',)


class MotionSerializer(ModelSerializer):
    """
    Serializer for motion.models.Motion objects.
    """
    active_version = PrimaryKeyRelatedField(read_only=True)
    log_messages = MotionLogSerializer(many=True, read_only=True)
    polls = MotionPollSerializer(many=True, read_only=True)
    reason = CharField(allow_blank=True, required=False, write_only=True)
    state = StateSerializer(read_only=True)
    text = CharField(write_only=True)
    title = CharField(max_length=255, write_only=True)
    versions = MotionVersionSerializer(many=True, read_only=True)
    workflow = IntegerField(min_value=1, required=False, validators=[validate_workflow_field])

    class Meta:
        model = Motion
        fields = (
            'id',
            'identifier',
            'title',
            'text',
            'reason',
            'versions',
            'active_version',
            'parent',
            'category',
            'submitters',
            'supporters',
            'state',
            'workflow',
            'tags',
            'attachments',
            'polls',
            'log_messages',)
        read_only_fields = ('parent',)  # Some other fields are also read_only. See definitions above.

    @transaction.atomic
    def create(self, validated_data):
        """
        Customized method to create a new motion from some data.
        """
        motion = Motion()
        motion.title = validated_data['title']
        motion.text = validated_data['text']
        motion.reason = validated_data.get('reason', '')
        motion.identifier = validated_data.get('identifier')
        motion.category = validated_data.get('category')
        motion.reset_state(validated_data.get('workflow', int(config['motions_workflow'])))
        motion.save()
        if validated_data['submitters']:
            motion.submitters.add(*validated_data['submitters'])
        else:
            motion.submitters.add(validated_data['request_user'])
        motion.supporters.add(*validated_data['supporters'])
        motion.attachments.add(*validated_data['attachments'])
        motion.tags.add(*validated_data['tags'])
        return motion

    @transaction.atomic
    def update(self, motion, validated_data):
        """
        Customized method to update a motion.
        """
        # Identifier and category.
        for key in ('identifier', 'category'):
            if key in validated_data.keys():
                setattr(motion, key, validated_data[key])

        # Workflow.
        workflow = validated_data.get('workflow')
        if workflow is not None and workflow != motion.workflow:
            motion.reset_state(workflow)

        # Decide if a new version is saved to the database.
        if (motion.state.versioning and
                not validated_data.get('disable_versioning', False)):  # TODO
            version = motion.get_new_version()
        else:
            version = motion.get_last_version()

        # Title, text, reason.
        for key in ('title', 'text', 'reason'):
            if key in validated_data.keys():
                setattr(version, key, validated_data[key])

        motion.save(use_version=version)

        # Submitters, supporters, attachments and tags
        for key in ('submitters', 'supporters', 'attachments', 'tags'):
            if key in validated_data.keys():
                attr = getattr(motion, key)
                attr.clear()
                attr.add(*validated_data[key])

        return motion
