from django.db import models
from wagtail.admin.panels import (
    FieldPanel,
    TabbedInterface,
    ObjectList,
)
from wagtail.blocks import StreamBlock
from wagtail.fields import StreamField
from wagtail.images.blocks import ImageChooserBlock

from books.managers import ContentBlockManager
from core.logger import get_logger
from core.wagtail_utils import get_default_settings
from books.blocks import (
    CMSDocumentBlock,
    SolutionBlock,
    TextBlock,
    BasicKnowledgeBlock,
    LinkBlock,
    VideoBlock,
    DocumentBlock,
    ImageUrlBlock,
    AssignmentBlock,
    InfogramBlock,
    GeniallyBlock,
    SubtitleBlock,
    SurveyBlock,
    ModuleRoomSlugBlock,
    ThinglinkBlock,
    InstructionBlock,
)
from core.wagtail_utils import StrictHierarchyPage
from notes.models import ContentBlockBookmark
from users.models import SchoolClass, User
from core.mixins import GraphqlNodeMixin

logger = get_logger(__name__)


def duplicate_entities_generator(new_module):
    def duplicate_entities(block):
        content_type = block.block_type
        value = block.value
        # logger.debug(block)
        if content_type == "assignment":
            assignment = value.get("assignment_id")
            if assignment is None:
                return None
            # copy the assignment
            assignment.pk = None
            assignment.title = f"{assignment.title} (Kopie)"
            assignment.module = new_module
            # logger.debug(f"setting new module {new_module}, {assignment.module}")
            assignment.save()
            data = {"assignment_id": assignment}
            new_block = ("assignment", data)
            return new_block
        if content_type == "survey":
            # logger.debug(value)
            survey = value.get("survey_id")
            if survey is None:
                return None
            # copy the survey
            survey.pk = None
            survey.title = f"{survey.title} (Kopie)"
            survey.module = new_module
            # logger.debug(f"setting new module {new_module}, {survey.module}")
            survey.save()
            data = {"survey_id": survey}
            new_block = ("survey", data)
            # logger.debug(new_block)
            return new_block
        return block

    return duplicate_entities


class ContentBlock(StrictHierarchyPage, GraphqlNodeMixin):
    class Meta:
        verbose_name = "Inhaltsblock"
        verbose_name_plural = "Inhaltsblöcke"

    NORMAL = "normal"
    TASK = "task"
    INSTRUMENT = "instrument"

    TYPE_CHOICES = (
        (NORMAL, "Normal"),
        (TASK, "Auftrag"),
        (INSTRUMENT, "Instrument"),
    )

    # blocks without owner are visible by default, need to be hidden for each class
    hidden_for = models.ManyToManyField(
        SchoolClass, related_name="hidden_content_blocks"
    )
    # blocks with owner are hidden by default, need to be shown for each class
    visible_for = models.ManyToManyField(
        SchoolClass, related_name="visible_content_blocks"
    )
    user_created = models.BooleanField(default=False)
    original_creator = models.ForeignKey(
        User, null=True, blank=True, default=None, on_delete=models.SET_NULL
    )

    bookmarks = models.ManyToManyField(
        User, through=ContentBlockBookmark, related_name="bookmarked_content_blocks"
    )

    content_blocks = [
        ("text_block", TextBlock()),
        ("basic_knowledge", BasicKnowledgeBlock()),
        ("assignment", AssignmentBlock()),
        ("survey", SurveyBlock()),
        ("image_block", ImageChooserBlock()),
        ("image_url_block", ImageUrlBlock()),
        ("link_block", LinkBlock()),
        # ('solution', TextBlock(icon='tick')),
        ("solution", SolutionBlock()),
        ("video_block", VideoBlock()),
        ("document_block", DocumentBlock()),
        ("infogram_block", InfogramBlock()),
        ("genially_block", GeniallyBlock()),
        ("thinglink_block", ThinglinkBlock()),
        ("subtitle", SubtitleBlock()),
        ("instruction", InstructionBlock()),
        ("module_room_slug", ModuleRoomSlugBlock()),
        # ('cms_document_block', DocumentChooserBlock(label='CMS Document'))
        ("cms_document_block", CMSDocumentBlock()),
    ]

    content_list_item = StreamBlock(content_blocks)
    contents = StreamField(
        content_blocks + [("content_list_item", content_list_item)],
        null=True,
        blank=True,
        use_json_field=True,
    )

    type = models.CharField(max_length=100, choices=TYPE_CHOICES, default=NORMAL)

    content_panels = [
        FieldPanel("title", classname="full title"),
        FieldPanel("type"),
        FieldPanel("contents"),
    ]

    #
    edit_handler = TabbedInterface(
        [ObjectList(content_panels, heading="Content"), get_default_settings()]
    )

    parent_page_types = ["books.Chapter"]
    subpage_types = []
    # prevent these fields from being copied
    exclude_fields_in_copy = ["bookmarks"]

    objects = ContentBlockManager()

    @property
    def module(self):
        return self.get_parent().get_parent().specific

    @property
    def route(self):  # path is probably used by treebeard
        return f"module/{self.module.slug}#{self.graphql_id}"

    # duplicate all attached Surveys and Assignments
    def duplicate_attached_entities(self):
        duplicate_entities_func = duplicate_entities_generator(self.module)
        new_contents = [duplicate_entities_func(content) for content in self.contents]
        cleaned_contents = [item for item in new_contents if item is not None]
        # we can't just insert a list here, we need a StreamValue data type
        # so we need to clear the list, then add each element in turn
        self.contents.clear()
        # like this, the internal methods of the SteamValue data type can work on the single elements
        for content in cleaned_contents:
            self.contents.append(content)

        # as an illustration
        # data = {'text': 'This is me'}
        # self.contents.append(('solution', data))
        self.save()

    def is_hidden_for_class(self, school_class):
        return (
            not self.user_created
            and self.hidden_for.filter(id=school_class.id).exists()
        ) or (
            self.user_created
            and not self.visible_for.filter(id=school_class.id).exists()
        )

    def reassign_entities(self):
        module = self.module
        logger.debug("reassigning entities")
        for content in self.contents:
            if content.block_type == "assignment":
                assignment = content.value.get("assignment_id")
                logger.debug(assignment.module)
                if assignment.module != module:
                    assignment.module = module
                    assignment.save()
            if content.block_type == "survey":
                survey = content.value.get("survey_id")
                logger.debug(survey.module)
                if survey.module != module:
                    survey.module = module
                    survey.save()

    # def save(self, *args, **kwargs):
    # todo: move this to the after_create_page and after_edit_page hooks, and remove from here.
    # for data in self.contents.raw_data:
    #     block_type, value = get_type_and_value(data)

    # todo: also do the same for assignments
    # if block_type == "survey":
    #     module = self.module
    #     survey = value["survey_id"]
    #     if isinstance(survey, int):
    #         survey = Survey.objects.get(pk=survey)
    #     if survey.module != module:
    #         survey.module = module
    #         survey.save()
    # super().save(*args, **kwargs)


class ContentBlockSnapshot(ContentBlock):
    hidden = models.BooleanField(default=False)
    snapshot = models.ForeignKey(
        "books.snapshot",
        on_delete=models.SET_NULL,
        null=True,
        related_name="custom_content_blocks",
    )

    def to_regular_content_block(self, owner, school_class):
        cb = ContentBlock(
            contents=self.contents,
            type=self.type,
            title=self.title,
            owner=owner,
            original_creator=self.original_creator,
            user_created=True,
        )
        self.add_sibling(instance=cb, pos="right")
        # some wagtail magic
        revision = cb.save_revision()
        revision.publish()
        cb.visible_for.add(school_class)
        cb.save()

        return cb
