import json
from builtins import PermissionError

import graphene
from api.utils import get_by_id, get_object
from basicknowledge.models import BasicKnowledge
from books.models import Chapter, ContentBlock, Module
from django.db import IntegrityError
from graphene import relay
from graphql_relay import from_global_id
from core.logger import get_logger
from notes.inputs import (
    AddContentHighlightArgument,
    AddHighlightArgument,
    AddNoteArgument,
    UpdateNoteArgument,
)
from notes.models import (
    ChapterBookmark,
    ContentBlockBookmark,
    Highlight,
    InstrumentBookmark,
    ModuleBookmark,
    Note,
)
from notes.schema import HighlightNode, NoteNode

logger = get_logger(__name__)


class UpdateContentBookmark(relay.ClientIDMutation):
    class Input:
        uuid = graphene.UUID(required=True)
        content_block = graphene.ID(required=True)
        bookmarked = graphene.Boolean(required=True)

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

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        uuid = kwargs.get("uuid")
        user = info.context.user
        content_block_id = kwargs.get("content_block")
        bookmarked = kwargs.get("bookmarked")

        content_block = get_object(ContentBlock, content_block_id)

        if bookmarked:
            try:
                ContentBlockBookmark.objects.create(
                    content_block=content_block, uuid=uuid, user=user
                )
            except IntegrityError:
                # already exists, probably fine
                return cls(success=True)
        else:
            ContentBlockBookmark.objects.get(
                content_block=content_block, uuid=uuid, user=user
            ).delete()

        return cls(success=True)


class AddNote(relay.ClientIDMutation):
    class Input:
        note = graphene.Argument(AddNoteArgument)

    note = graphene.Field(NoteNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        note = kwargs.get("note")
        content_uuid = note.get("content", "")
        content_block_id = note.get("block", "")
        parent = note.get("parent")
        text = note.get("text")

        if content_uuid != "":
            type = note.get("type")
            if type == "ContentBlockNode":
                content_block = get_object(ContentBlock, content_block_id)

                bookmark = ContentBlockBookmark.objects.get(
                    content_block=content_block, uuid=content_uuid, user=user
                )
            else:
                instrument = BasicKnowledge.objects.get(slug=content_block_id)
                bookmark = InstrumentBookmark.objects.get(
                    instrument=instrument, uuid=content_uuid, user=user
                )
        else:
            type = note.get("type", "chapter")
            if type == "module":
                bookmark = ModuleBookmark.objects.get(module__slug=parent, user=user)
            else:
                _, id = from_global_id(parent)
                bookmark = ChapterBookmark.objects.get(chapter__id=id, user=user)

        bookmark.note = Note.objects.create(text=text)
        bookmark.save()

        return cls(note=bookmark.note)


class UpdateNote(relay.ClientIDMutation):
    class Input:
        note = graphene.Argument(UpdateNoteArgument)

    note = graphene.Field(NoteNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        note = kwargs.get("note")
        id = note.get("id")
        text = note.get("text")
        note = get_object(Note, id)

        if (
            hasattr(note, "contentblockbookmark")
            and note.contentblockbookmark.user != user
            or hasattr(note, "chapterbookmark")
            and note.chapterbookmark.user != user
            or hasattr(note, "modulebookmark")
            and note.modulebookmark.user != user
        ):
            raise PermissionError

        note.text = text
        note.save()
        return cls(note=note)


class UpdateChapterBookmark(relay.ClientIDMutation):
    class Input:
        chapter = graphene.ID(required=True)
        bookmarked = graphene.Boolean(required=True)

    success = graphene.Boolean()

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user
        chapter_id = kwargs.get("chapter")
        bookmarked = kwargs.get("bookmarked")

        chapter = get_object(Chapter, chapter_id)

        if bookmarked:
            ChapterBookmark.objects.create(chapter=chapter, user=user)
        else:
            ChapterBookmark.objects.get(chapter=chapter, user=user).delete()

        return cls(success=True)


class UpdateModuleBookmark(relay.ClientIDMutation):
    class Input:
        module = graphene.String(required=True)
        bookmarked = graphene.Boolean(required=True)

    success = graphene.Boolean()

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user
        module_slug = kwargs.get("module")
        bookmarked = kwargs.get("bookmarked")

        module = Module.objects.get(slug=module_slug)

        if bookmarked:
            ModuleBookmark.objects.create(module=module, user=user)
        else:
            ModuleBookmark.objects.get(module=module, user=user).delete()

        return cls(success=True)


class UpdateInstrumentBookmark(relay.ClientIDMutation):
    class Input:
        uuid = graphene.UUID(required=True)
        instrument = graphene.String(required=True)
        bookmarked = graphene.Boolean(required=True)

    success = graphene.Boolean()

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user
        instrument_slug = kwargs.get("instrument")
        uuid = kwargs.get("uuid")
        bookmarked = kwargs.get("bookmarked")

        instrument = BasicKnowledge.objects.get(slug=instrument_slug)

        if bookmarked:
            try:
                InstrumentBookmark.objects.create(
                    instrument=instrument, uuid=uuid, user=user
                )
            except IntegrityError:
                # already exists, that's probably ok
                return cls(success=True)
        else:
            InstrumentBookmark.objects.get(
                instrument=instrument, uuid=uuid, user=user
            ).delete()

        return cls(success=True)


class UpdateHighlight(relay.ClientIDMutation):
    class Input:
        color = graphene.String()
        note = graphene.String()
        id = graphene.ID(required=True)

    highlight = graphene.Field(HighlightNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        color = kwargs.get("color")
        note = kwargs.get("note")
        id = kwargs.get("id")

        highlight = get_object(Highlight, id)

        if highlight is None:
            raise Exception("Highlight not found")

        if highlight.user != user:
            raise PermissionError

        if note is None and color is None:
            return cls(highlight=highlight)

        if color is not None:
            highlight.color = color
        if note is not None:
            if highlight.note is None:
                highlight.note = Note.objects.create(text=note)
            else:
                highlight.note.text = note
        highlight.save()

        return cls(highlight=highlight)


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

    success = graphene.Boolean(required=True)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        id = kwargs.get("id")

        highlight = get_object(Highlight, id)

        if highlight is None:
            raise Exception("Highlight not found")

        if highlight.user != user:
            raise PermissionError

        highlight.delete()

        return cls(success=True)


class AddHighlight(relay.ClientIDMutation):
    class Input:
        highlight = graphene.Argument(AddHighlightArgument)

    highlight = graphene.Field(HighlightNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        highlight = kwargs.get("highlight")

        if highlight is None:
            raise Exception("No highlight provided, should not happen")

        page_id = highlight.get("page")
        paragraph_index = highlight.get("paragraph_index")
        text = highlight.get("text")
        start_position = highlight.get("start_position")
        selection_length = highlight.get("selection_length")
        color = highlight.get("color")

        page_type, page_id = from_global_id(page_id)
        if page_type == "ModuleNode":
            page = Module.objects.get(id=page_id)
        elif page_type == "ChapterNode":
            page = Chapter.objects.get(id=page_id)
        elif page_type == "InstrumentNode":
            page = BasicKnowledge.objects.get(id=page_id)
        else:
            raise Exception("wrong type")

        logger.debug(page)
        new_highlight = Highlight.objects.create(
            page=page,
            user=user,
            paragraph_index=paragraph_index,
            text=text,
            start_position=start_position,
            selection_length=selection_length,
            color=color,
        )
        logger.debug(new_highlight)
        logger.debug(new_highlight.page)

        return cls(highlight=new_highlight)


class AddContentHighlight(relay.ClientIDMutation):
    class Input:
        highlight = graphene.Argument(AddContentHighlightArgument)

    highlight = graphene.Field(HighlightNode)

    @classmethod
    def mutate_and_get_payload(cls, root, info, **kwargs):
        user = info.context.user

        highlight = kwargs.get("highlight")

        if highlight is None:
            raise Exception("No highlight provided, should not happen")

        logger.debug(highlight)
        page_id = highlight.get("page")
        content_index = highlight.get("content_index")
        content_uuid = highlight.get("content_uuid")
        paragraph_index = highlight.get("paragraph_index")
        text = highlight.get("text")
        start_position = highlight.get("start_position")
        selection_length = highlight.get("selection_length")
        color = highlight.get("color")

        page_type, page_id = from_global_id(page_id)
        if page_type == "ContentBlockNode":
            page = ContentBlock.objects.get(id=page_id)
        elif page_type == "InstrumentNode":
            page = BasicKnowledge.objects.get(id=page_id)
        else:
            raise Exception("wrong type")

        new_highlight = Highlight.objects.create(
            page=page,
            user=user,
            content_index=content_index,
            content_uuid=content_uuid,
            paragraph_index=paragraph_index,
            text=text,
            start_position=start_position,
            selection_length=selection_length,
            color=color,
        )

        return cls(highlight=new_highlight)


class NoteMutations:
    add_note = AddNote.Field()
    add_highlight = AddHighlight.Field()
    add_content_highlight = AddContentHighlight.Field()
    delete_highlight = DeleteHighlight.Field()
    update_highlight = UpdateHighlight.Field()
    update_note = UpdateNote.Field()
    update_content_bookmark = UpdateContentBookmark.Field()
    update_chapter_bookmark = UpdateChapterBookmark.Field()
    update_module_bookmark = UpdateModuleBookmark.Field()
    update_instrument_bookmark = UpdateInstrumentBookmark.Field()
