import graphene
from django.core.exceptions import PermissionDenied
from graphene import relay
from graphql_relay import to_global_id

from api.utils import get_object
from rooms.inputs import UpdateRoomArgument, AddRoomArgument, AddRoomEntryArgument, UpdateRoomEntryArgument
from rooms.models import Comment, Room, RoomEntry
from rooms.schema import CommentNode, RoomNode, RoomEntryNode
from rooms.serializers import RoomSerializer, RoomEntrySerializer
from users.models import SchoolClass, SchoolClassMember


class MutateRoom(relay.ClientIDMutation):
    errors = graphene.List(graphene.String)
    room = graphene.Field(RoomNode)

    @classmethod
    def mutate_and_get_payload(cls, *args, **kwargs):
        room_data = kwargs.get('room')
        school_class = get_object(SchoolClass, room_data.get('school_class').get('id'))
        room_data['school_class'] = school_class.id
        if room_data.get('id') is not None:
            room = get_object(Room, room_data['id'])
            serializer = RoomSerializer(room, data=room_data)
        else:
            serializer = RoomSerializer(data=room_data)
        if serializer.is_valid():
            serializer.save()
            return cls(room=serializer.instance, errors=None)

        return cls(room=None, errors=['{}: {}'.format(key, value) for key, value in serializer.errors.items()])


class AddRoom(MutateRoom):
    class Input:
        room = graphene.Argument(AddRoomArgument)  # NB: can't be named AddRoomInput, otherwise graphene complains

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user
        if not user.has_perm('users.can_manage_school_class_content'):
            return cls(room=None, errors=['not allowed'])

        room_data = kwargs.get('room')
        school_class_id = user.selected_class.id
        room_data['school_class'] = school_class_id
        serializer = RoomSerializer(data=room_data)
        if serializer.is_valid():
            serializer.save()
            return cls(room=serializer.instance)

        return cls(room=None, errors=['{}: {}'.format(key, value) for key, value in serializer.errors.items()])


class UpdateRoom(MutateRoom):
    class Input:
        room = graphene.Argument(UpdateRoomArgument)  # NB: can't be named ChangeRoomInput, otherwise graphene complains


class DeleteRoom(relay.ClientIDMutation):
    class Input:
        id = graphene.ID(required=True)

    success = graphene.Boolean()
    errors = graphene.List(graphene.String)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        id = kwargs.get('id')
        room = get_object(Room, id)
        user = info.context.user
        if not user.has_perm('users.can_manage_school_class_content'):
            raise PermissionDenied('Missing permissions')
        if room.school_class not in user.school_classes.all():
            raise PermissionDenied('Permission denied: Incorrect school classes')
        room.delete()
        return cls(success=True)


class MutateRoomEntry(relay.ClientIDMutation):
    room_entry = graphene.Field(RoomEntryNode)
    errors = graphene.List(graphene.String)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        room_entry_data = kwargs.get('room_entry')

        if room_entry_data.get('room') is not None:
            room_entry_data['room'] = get_object(Room, room_entry_data.get('room')).id

        if room_entry_data.get('id') is not None:
            # update path
            instance = get_object(RoomEntry, room_entry_data.get('id'))

            if not instance.room.school_class.is_user_in_schoolclass(info.context.user):
                raise Exception('You are in the wrong class')

            if instance.author.pk != info.context.user.pk:
                raise Exception('You are not the author')

            serializer = RoomEntrySerializer(instance, data=room_entry_data, partial=True)
        else:
            # add path
            room_entry_data['author'] = info.context.user.pk
            serializer = RoomEntrySerializer(data=room_entry_data)

        if serializer.is_valid():
            serializer.save()

            return cls(room_entry=serializer.instance)

        return cls(room_entry=None, errors=['{}: {}'.format(key, value) for key, value in serializer.errors.items()])


class AddRoomEntry(MutateRoomEntry):
    class Input:
        room_entry = graphene.Argument(AddRoomEntryArgument)


class UpdateRoomEntry(MutateRoomEntry):
    class Input:
        room_entry = graphene.Argument(UpdateRoomEntryArgument)


class DeleteRoomEntry(relay.ClientIDMutation):
    class Input:
        id = graphene.ID(required=True)

    success = graphene.Boolean()
    room_slug = graphene.String()
    room_id = graphene.ID()
    errors = graphene.List(graphene.String)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        id = kwargs.get('id')
        room_entry = get_object(RoomEntry, id)
        if room_entry.author.pk != info.context.user.pk:
            raise Exception('You are not the owner of this room entry')
        room_id = to_global_id('RoomNode', room_entry.room.pk)
        room_slug = room_entry.room.slug
        room_entry.delete()
        return cls(success=True, room_id=room_id, room_slug=room_slug)


class UpdateRoomVisibility(relay.ClientIDMutation):
    class Input:
        id = graphene.ID(required=True)
        restricted = graphene.Boolean(required=True)

    success = graphene.Boolean()
    room = graphene.Field(RoomNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        id = kwargs.get('id')
        restricted = kwargs.get('restricted')
        user = info.context.user
        room = get_object(Room, id)
        if not user.is_teacher() or not SchoolClassMember.objects.filter(active=True,user=user,school_class=room.school_class).exists():
            raise Exception('You are not permitted to do this')
        room.restricted = restricted
        room.save()
        return cls(success=True, room=room)



class AddComment(relay.ClientIDMutation):
    class Input:
        comment = graphene.String(required=True)
        room_entry = graphene.ID(required=True)

    success = graphene.Boolean()
    comment = graphene.Field(CommentNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **args):
        room_entry_id = args.get('room_entry')
        text = args.get('comment')
        user = info.context.user

        room_entry = get_object(RoomEntry, room_entry_id)
        comment = Comment.objects.create(text=text, room_entry=room_entry, owner=user)

        return cls(success=True, comment=comment)



class RoomMutations:
    update_room = UpdateRoom.Field()
    add_room = AddRoom.Field()
    delete_room = DeleteRoom.Field()
    add_room_entry = AddRoomEntry.Field()
    delete_room_entry = DeleteRoomEntry.Field()
    update_room_entry = UpdateRoomEntry.Field()
    add_comment = AddComment.Field()
    update_room_visibility = UpdateRoomVisibility.Field()
