from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from django.db import IntegrityError

from orchestra.models import Todo
from orchestra.models import TodoQA
from orchestra.models import TodoListTemplate
from orchestra.json_schemas.todos import TodoListSchema
from orchestra.json_schemas.todos import TodoActionListSchema
from orchestra.utils.mixins import JSONSchemaValidationMixin
from orchestra.utils.common_helpers import get_step_by_project_id_and_step_slug


class TodoQASerializer(serializers.ModelSerializer):

    class Meta:
        model = TodoQA
        fields = (
            'id',
            'created_at',
            'todo',
            'approved',
            'comment')
        read_only_fields = ('id',)


class TodoListTemplateSerializer(serializers.ModelSerializer,
                                 JSONSchemaValidationMixin):
    json_schemas = {
        'todos': TodoListSchema
    }

    class Meta:
        model = TodoListTemplate
        fields = (
            'id',
            'created_at',
            'slug',
            'name',
            'description',
            'creator',
            'todos')
        read_only_fields = ('id',)


class TodoBulkCreateListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        result = [self.child.create(attrs) for attrs in validated_data]
        try:
            self.child.Meta.model.objects.bulk_create(result)
        except IntegrityError as e:
            raise ValidationError(e)
        return result

    def update(self, instances, validated_data):
        result = []
        for instance, data in zip(instances, validated_data):
            result.append(self.child.update(instance, data))

        writable_fields = [
            x for x in self.child.Meta.fields
            if x not in self.child.Meta.read_only_fields
        ]

        try:
            self.child.Meta.model.objects.bulk_update(result, writable_fields)
        except IntegrityError as e:
            raise ValidationError(e)

        return result


class StepField(serializers.Field):
    def to_representation(self, data):
        return data.slug

    def to_internal_value(self, value):
        return value


class BulkTodoSerializer(serializers.ModelSerializer):
    json_schemas = {
        'activity_log': TodoActionListSchema
    }

    step = StepField()
    additional_data = serializers.JSONField(required=False)

    # TODO(murat): Remove this validation when project
    # becomes a required field in models
    def validate(self, data):
        step = data.get('step')
        project = data.get('project')
        if step is not None and project is None:
            raise serializers.ValidationError({
                'project': [
                    'if step is given, project should also be supplied.']})
        return data

    def _set_step_to_validated_data(self, validated_data):
        project_id = validated_data['project'].id
        step = get_step_by_project_id_and_step_slug(project_id,
                                                    validated_data['step'])
        validated_data['step'] = step
        return validated_data

    def create(self, validated_data):
        validated_data = self._set_step_to_validated_data(validated_data)
        instance = Todo(**validated_data)
        if isinstance(self._kwargs['data'], dict):
            instance.save()
        return instance

    def update(self, instance, validated_data):
        if validated_data.get('step') is not None:
            validated_data = self._set_step_to_validated_data(validated_data)
        for k, v in validated_data.items():
            setattr(instance, k, v)

        if isinstance(self._kwargs['data'], dict):
            instance.save()
        return instance

    class Meta:
        model = Todo
        fields = (
            'id',
            'created_at',
            'title',
            'details',
            'parent_todo',
            'template',
            'start_by_datetime',
            'due_datetime',
            'activity_log',
            'section',
            'project',
            'step',
            'slug',
            'order',
            'status',
            'additional_data',
            'required',
            'is_deleted')
        read_only_fields = ('id',)
        list_serializer_class = TodoBulkCreateListSerializer


class BulkTodoSerializerWithoutQA(BulkTodoSerializer):
    qa = serializers.SerializerMethodField()

    def get_qa(self, obj):
        return None

    class Meta(BulkTodoSerializer.Meta):
        fields = BulkTodoSerializer.Meta.fields + ('qa',)
        read_only_fields = ('id',)


class BulkTodoSerializerWithQA(BulkTodoSerializer):
    qa = TodoQASerializer(read_only=True)

    class Meta(BulkTodoSerializer.Meta):
        fields = BulkTodoSerializer.Meta.fields + ('qa',)
        read_only_fields = ('id',)
