import graphene
from graphene import relay
from rest_framework.exceptions import PermissionDenied

from api.utils import get_object
from portfolio.inputs import AddProjectArgument, AddProjectEntryArgument, UpdateProjectArgument, \
    UpdateProjectEntryArgument
from portfolio.models import Project, ProjectEntry
from portfolio.schema import ProjectEntryNode, ProjectNode
from portfolio.serializers import ProjectEntrySerializer, ProjectSerializer


def check_owner(user, project):
    return user.id != project.student.id


class MutateProject(relay.ClientIDMutation):
    errors = graphene.List(graphene.String)
    project = graphene.Field(ProjectNode)

    # class Meta:
    #     property = 'project'
    #     serializer_class = ProjectSerializer

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        data = kwargs.get('project')
        data['student'] = info.context.user.id

        if data.get('id') is not None:
            entity = get_object(Project, data['id'])
            serializer = ProjectSerializer(entity, data=data)
        else:
            serializer = ProjectSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            props = {
                'project': serializer.instance,
                'errors': None
            }
            return cls(**props)

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


class AddProject(MutateProject):
    class Input:
        project = graphene.Argument(
            AddProjectArgument)  # NB: can't be named AddProjectInput, otherwise graphene complains

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        data = kwargs.get('project')
        data['student'] = info.context.user.id

        serializer = ProjectSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return cls(project=serializer.instance)

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


class UpdateProject(MutateProject):
    class Input:
        project = graphene.Argument(UpdateProjectArgument)


class MutateProjectEntry(relay.ClientIDMutation):
    errors = graphene.List(graphene.String)
    project_entry = graphene.Field(ProjectEntryNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        data = kwargs.get('project_entry')
        project = None

        if data.get('project') is not None:
            project = get_object(Project, data.get('project'))
            data['project'] = project.id
            if check_owner(info.context.user, project):
                return cls(project_entry=None, errors=['not allowed'])

        if data.get('id') is not None:
            entity = get_object(ProjectEntry, data['id'])
            if check_owner(info.context.user, entity.project):
                return cls(project_entry=None, errors=['not allowed'])
            serializer = ProjectEntrySerializer(entity, data=data, partial=True)
        else:
            serializer = ProjectEntrySerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return cls(project_entry=serializer.instance, errors=None)

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


class AddProjectEntry(MutateProjectEntry):
    class Input:
        project_entry = graphene.Argument(AddProjectEntryArgument)


class UpdateProjectEntry(MutateProjectEntry):
    class Input:
        project_entry = graphene.Argument(UpdateProjectEntryArgument)


class DeleteProject(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')
        project = get_object(Project, id)
        user = info.context.user

        if project.student != user:
            raise PermissionDenied('Permission denied: Incorrect project')
        project.delete()
        return cls(success=True)


class DeleteProjectEntry(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')
        project_entry = get_object(ProjectEntry, id)
        user = info.context.user

        if project_entry.project.student != user:
            raise PermissionDenied('Permission denied: Incorrect project')
        project_entry.delete()
        return cls(success=True)


class UpdateProjectSharedState(relay.ClientIDMutation):
    class Input:
        id = graphene.ID()
        shared = graphene.Boolean()

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

    @classmethod
    def mutate_and_get_payload(cls, root, info, **args):
        try:
            id = args.get('id')
            shared = args.get('shared')
            user = info.context.user

            project = get_object(Project, id)
            if project.student != user:
                raise PermissionError()
            project.final = shared
            project.save()

            return cls(success=True, shared=shared)

        except PermissionError:
            errors = ["You don't have the permission to do that."]
        except Exception as e:
            errors = ['Error: {}'.format(e)]

        return cls(success=False, shared=None, errors=errors)


class PortfolioMutations:
    add_project = AddProject.Field()
    update_project = UpdateProject.Field()
    delete_project = DeleteProject.Field()
    add_project_entry = AddProjectEntry.Field()
    update_project_entry = UpdateProjectEntry.Field()
    delete_project_entry = DeleteProjectEntry.Field()
    update_project_shared_state = UpdateProjectSharedState.Field()
