import graphene
from django.db.models import Q
from graphene import relay, ObjectType
from graphene_django import DjangoObjectType

from books.models.snapshot import Snapshot
from ..interfaces import ChapterInterface
from ..interfaces.contentblock import ContentBlockInterface
from ...models import ContentBlock


class SnapshotContentBlock:
    def __init__(self, content_block, snapshot):
        self.id = content_block.id
        self.pk = content_block.pk
        self.title = content_block.title
        self.contents = content_block.contents
        self.type = content_block.type
        self.hidden = snapshot.hidden_content_blocks.filter(id=content_block.id).exists() or (
            hasattr(content_block, 'contentblocksnapshot') and content_block.contentblocksnapshot.hidden
        )


class SnapshotChapter:
    def __init__(self, chapter, snapshot, description_hidden=False, title_hidden=False):
        self.id = chapter.id
        self.pk = chapter.pk
        self.title = chapter.title
        self.description = chapter.description
        self.title_hidden = title_hidden
        self.description_hidden = description_hidden
        self.content_blocks = []
        base_qs = ContentBlock.get_by_parent(chapter)
        default_content = Q(Q(user_created=False) & Q(contentblocksnapshot__snapshot__isnull=True))
        this_snapshot = Q(contentblocksnapshot__snapshot=snapshot)
        self.content_blocks = [
            SnapshotContentBlock(
                content_block=content_block,
                snapshot=snapshot
            ) for content_block in
            base_qs.filter(default_content | this_snapshot)
        ]
        # all from module without owner
        # all with snapshotcontentblock with this snapshot


class SnapshotObjective:
    def __init__(self, objective, snapshot):
        self.id = objective.id
        self.text = objective.text
        self.hidden = snapshot.hidden_objectives.filter(id=objective.id).exists() or (
            hasattr(objective, 'objectivesnapshot') and objective.objectivesnapshot.hidden
        )


class SnapshotObjectiveGroup:
    def __init__(self, objective_group, hidden, snapshot):
        self.title = objective_group.title
        self.display_title = objective_group.get_title_display()
        self.hidden = hidden
        self.id = objective_group.id
        base_qs = objective_group.objectives
        default = Q(Q(owner__isnull=True) & Q(objectivesnapshot__snapshot__isnull=True))
        this_snapshot = Q(objectivesnapshot__snapshot=snapshot)
        self.objectives = [
            SnapshotObjective(
                objective=objective,
                snapshot=snapshot
            )
            for objective in base_qs.filter(default | this_snapshot)
        ]


class SnapshotContentBlockNode(ObjectType):
    class Meta:
        interfaces = (relay.Node, ContentBlockInterface,)

    hidden = graphene.Boolean()


class SnapshotChapterNode(ObjectType):
    class Meta:
        interfaces = (relay.Node, ChapterInterface,)

    content_blocks = graphene.List(SnapshotContentBlockNode)
    description_hidden = graphene.Boolean()
    title_hidden = graphene.Boolean()


class SnapshotChangesNode(ObjectType):
    hidden_objectives = graphene.Int(required=True)
    new_objectives = graphene.Int(required=True)
    hidden_content_blocks = graphene.Int(required=True)
    new_content_blocks = graphene.Int(required=True)


class SnapshotObjectiveNode(ObjectType):
    class Meta:
        interfaces = (relay.Node,)

    hidden = graphene.Boolean(required=True)
    text = graphene.String(required=True)


class SnapshotObjectiveGroupNode(ObjectType):
    class Meta:
        interfaces = (relay.Node,)

    title = graphene.String(required=True)
    hidden = graphene.Boolean(required=True)
    display_title = graphene.String(required=True)
    objectives = graphene.List(SnapshotObjectiveNode, required=True)


class SnapshotNode(DjangoObjectType):
    title = graphene.String()
    chapters = graphene.List(SnapshotChapterNode)
    meta_title = graphene.String()
    hero_image = graphene.String()
    changes = graphene.Field(SnapshotChangesNode)
    mine = graphene.Boolean()
    shared = graphene.Boolean(required=True)
    creator = graphene.String(required=True)
    objective_groups = graphene.List(SnapshotObjectiveGroupNode)

    class Meta:
        model = Snapshot
        interfaces = (relay.Node,)

    @staticmethod
    def resolve_chapters(parent, info, **kwargs):
        return [
            SnapshotChapter(
                chapter_snapshot.chapter,
                snapshot=parent,
                title_hidden=chapter_snapshot.title_hidden,
                description_hidden=chapter_snapshot.description_hidden
            )
            for chapter_snapshot in parent.chapters.through.objects.filter(snapshot=parent)
        ]

    @staticmethod
    def resolve_title(parent: Snapshot, info, **kwargs):
        return f'Snapshot {parent.id}'

    @staticmethod
    def resolve_meta_title(parent, info, **kwargs):
        return parent.module.meta_title

    @staticmethod
    def resolve_hero_image(parent, info, **kwargs):
        if parent.module.hero_image:
            return parent.module.hero_image.file.url
        return ''

    @staticmethod
    def resolve_changes(parent, info, **kwargs):
        return {
            'hidden_objectives': parent.hidden_objectives.count(),
            'new_objectives': parent.custom_objectives.filter(hidden=False).count(),
            'hidden_content_blocks': parent.hidden_content_blocks.count(),
            'new_content_blocks': parent.custom_content_blocks.filter(hidden=False).count()
        }

    @staticmethod
    def resolve_mine(parent, info, **kwargs):
        return parent.creator == info.context.user

    @staticmethod
    def resolve_creator(parent, info, **kwargs):
        return f'{parent.creator.first_name} {parent.creator.last_name}'

    @staticmethod
    def resolve_objective_groups(parent, info, **kwargs):
        return [
            SnapshotObjectiveGroup(
                objective_group=objective_group_snapshot.objective_group,
                hidden=objective_group_snapshot.hidden,
                snapshot=parent
            )
            for objective_group_snapshot in parent.objective_groups.through.objects.filter(snapshot=parent)
        ]
