import json
from logging import getLogger

from django.core.management import BaseCommand
from django.db import models
from django.db.models import Case, IntegerField

from books.management.commands.migrate_objectives_to_content import create_text_in_content_block, \
    create_content_block_snapshot_from_objective, \
    create_content_block_contents
from books.models import Chapter, ObjectiveGroupSnapshot, ContentBlockSnapshot, Snapshot, ChapterSnapshot
from books.models import ContentBlock
from books.models import Module
from objectives.models import Objective, ObjectiveSnapshot, ObjectiveGroup

logger = getLogger(__name__)


class Command(BaseCommand):
    def handle(self, *args, **options):
        """
        This command must be run after the migration of objectives to content blocks.
        Migrate objective snapshots to content blocks
        - Verlagsinhalte - deafult content blocks are referced in the snapshot by foreign key

        Man muss unterscheiden zwischen, snapshots die nur Verlagslernziele sichtbar und unsichtbar machen.
        Und solchen die auch benutzerdefinierte Lernziele sichtbar und unsichtbar machen.
        Es gibt keine custom objective groups!
        Es gibt keine hidden custom objective_groups

        Case1:
        - 100% Verlagslernziele, 100% Verlagsgruppe, nicht neue content blocks erstellen, nichts hidden... migration muss nichts machen.

        Case2:
        - 100% Verlagslernziele, 100% Verlagsgruppe, hidden gruppe, nur verlagsinhalt gruppe verstecken

        Case3:
        - Bestende gruppe mit benutzerdefinierten lernzielen, - custom content_block snapshot erstellen.
        - Einzelne verslagslernziele sind versteckt. - custom content_block snapshot erstellen.

        --- user created setzen bei custom content snapshot.

        """
        prefix = "SNAP "
        ContentBlock.objects.filter(title__startswith=prefix).delete()
        Chapter.objects.filter(title__startswith=prefix).delete()
        ContentBlockSnapshot.objects.filter(title__startswith=prefix).delete()
        ChapterSnapshot.objects.filter(chapter__title__startswith=prefix).delete()

        analyze()

        migrate_snapshots()


def migrate_snapshots():
    count = 0

    case1_count = 0
    case2_count = 0
    case3_count = 0

    createed_content_blocks = 0
    visible_objectives_by_ids = {}
    snapshot_counter = 0

    for module in Module.objects.all():
        for snapshot in Snapshot.objects.filter(module=module):
            group_counter = snapshot.objective_groups.through.objects.filter(objective_group__module=module,
                                                                             snapshot=snapshot).count()
            print(
                f"{snapshot_counter} Snapshot id: {snapshot.id} Module: {module.title} {group_counter} groups {snapshot.creator} {snapshot.title}")
            snapshot_counter += 1

            for objective_group_snapshot in get_objectives_group_snapshots_in_specific_order(module, snapshot):
                header = f"{count} {module.title:50} {objective_group_snapshot.objective_group.get_title_display():25} {str(snapshot.creator):40} {objective_group_snapshot.hidden} "
                count += 1
                objective_group = objective_group_snapshot.objective_group

                hidden_default_objectives = snapshot.hidden_objectives.filter(
                    group=objective_group)

                visible_custom_objectives = snapshot.custom_objectives.filter(snapshot=snapshot, hidden=False,
                                                                              group=objective_group_snapshot.objective_group)
                group_is_hidden = objective_group_snapshot.hidden

                info = f"{hidden_default_objectives.count()} {visible_custom_objectives.count()}"

                if (not hidden_default_objectives and not visible_custom_objectives and not group_is_hidden):
                    # print(f"{info}  Case 1 -  skip")
                    case1_count += 1
                    break

                if not hidden_default_objectives and not visible_custom_objectives and group_is_hidden:
                    print(header + f"Case 2 - {info} hide default content group")
                    case2_count += 1
                    content_block = get_content_block_by_objective_group(objective_group_snapshot,
                                                                         module, False, None)
                    snapshot.hidden_content_blocks.add(content_block.id)
                    snapshot.save()

                if hidden_default_objectives or visible_custom_objectives:
                    print(header + f"Case 3 - {info} create custom content blocks")
                    case3_count += 1

                    # Verlags Lernziele
                    visible_default_objectives = get_visible_default_objectives(objective_group, module, snapshot)
                    # Benutzerdefinierte Lernziele
                    visible_custom_objectives = list(snapshot.custom_objectives.filter(hidden=False))

                    visible_objectives = visible_default_objectives + visible_custom_objectives

                    # filter for unique texts in objectives
                    objectives_by_texts = {}
                    for objective in visible_objectives:
                        if objective.text not in objectives_by_texts:
                            objectives_by_texts[objective.text] = objective
                    visible_objectives = list(objectives_by_texts.values())

                    if visible_objectives:
                        # make combinations of objectives unique by text, this prevents generation of many duplicated content blocks
                        visible_objectives_hash = hash(
                            [objective.text for objective in visible_objectives].__str__())

                        visible_objectives_by_ids[visible_objectives_hash] = visible_objectives

                    for objectives in visible_objectives_by_ids.values():
                        print("")
                        for objective in objectives:
                            print(f"             Objective: {objective.group} {objective}   owner:{objective.owner}")
                        print("-")

                    print(f"      visible_objectives_by_ids: {len(visible_objectives_by_ids.items())}")

                    # create custom content blocks with the objectives
                    created_content_blocks = 0
                    chapter = module.get_first_child()
                    if "Lernziele" not in chapter.title:
                        raise Exception(f"Chapter does not contain 'Lernziele' first title is {chapter.title}")

                    # Owner des custom blocks festlegen
                    custom_content_block_snapshot = create_content_block_snapshot_from_objective(
                        objective_group, chapter, snapshot,
                        owner=snapshot.creator)

                    # Hide default content block for this objective group, since custom content block is created
                    default_content_block = get_default_content_block(objective_group_snapshot, module)
                    if default_content_block:
                        print(f"Default content block: {default_content_block.title}")
                        snapshot.hidden_content_blocks.add(default_content_block.id)

                    if list(visible_objectives_by_ids.values()):
                        objectives = list(visible_objectives_by_ids.values())[0]
                        create_text_in_content_block(objectives, custom_content_block_snapshot, get_or_create=True)
                        created_content_blocks += 1
                    snapshot.save()
    print()
    print(f"Skipped {case1_count} Case 1")
    print(f"Hidden default content groups {case2_count} Case 2")
    print(f"Created new content {case3_count} Case 3")


def get_content_block_by_objective_group(objective_group_snapshot, module, user_created: bool, user):
    # TODO: review qustion, is it necessary to filter for module
    content_block = ContentBlock.objects.filter(
        title__contains=objective_group_snapshot.objective_group.get_title_display(),
        user_created=user_created, owner=user).descendant_of(module)
    if content_block.count() > 1:
        return content_block.first()

    return content_block.first()


def get_default_content_block(objective_group_snapshot, module):
    default_objectives = Objective.objects.filter(group=objective_group_snapshot.objective_group,
                                                  group__module=module,
                                                  owner__isnull=True,
                                                  objectivesnapshot__isnull=True)

    default_content_block_contents = create_content_block_contents(default_objectives)

    contents = json.loads(default_content_block_contents)
    contents[0].get("value").get("text")
    chapter = Chapter.objects.filter(title__contains="Lernziele").descendant_of(module)
    text_contents = contents[0].get("value").get("text")
    group_title = objective_group_snapshot.objective_group.get_title_display()
    content_blocks = ContentBlock.objects.filter(user_created=False).descendant_of(chapter.first())

    for content_block in content_blocks:
        print(content_block.title, group_title)
        if group_title in content_block.title:
            return content_block

    if not content_block.exists():
        raise Exception("Content block does not exist ")


def get_objectives_group_snapshots_in_specific_order(module: Module, snapshot: Snapshot):
    # Create a specific order for the objective groups
    # https://stackoverflow.com/questions/5966462/sort-queryset-by-values-in-list
    # https://docs.djangoproject.com/en/5.0/ref/models/conditional-expressions/
    order_of_objective_groups = ["language_communication", "society", "interdisciplinary"]
    _whens = [models.When(objective_group__title=value, then=sort_index) for sort_index, value in
              enumerate(order_of_objective_groups)]

    qs = snapshot.objective_groups.through.objects.filter(
        objective_group__module=module,
        snapshot=snapshot
    ).annotate(
        _sort_index=Case(*_whens, default=models.Value(len(order_of_objective_groups)), output_field=IntegerField())
    ).order_by('_sort_index')
    return qs


def get_visible_default_objectives(objective_group, module, snapshot):
    default_objectives = Objective.objects.filter(group=objective_group,
                                                  group__module=module,
                                                  owner__isnull=True,
                                                  objectivesnapshot__isnull=True)
    hidden_default_objectives = snapshot.hidden_objectives.filter(group=objective_group)
    visible_default_objectives = [objective for objective in default_objectives if
                                  objective.id not in hidden_default_objectives.values_list("id",
                                                                                            flat=True)]
    return visible_default_objectives


def analyze():
    print(f"""
    OjectiveGroups: {ObjectiveGroup.objects.count()}
    Objectives: {Objective.objects.count()}

    ObjectiveGroupSnapshots: {ObjectiveGroupSnapshot.objects.count()}
    ObjectivesSnapshots: {ObjectiveSnapshot.objects.count()}


    ObjectiveGroups: {ObjectiveGroup.objects.filter(objectivegroupsnapshot__isnull=True).count()}
    Objectives: {Objective.objects.filter(objectivesnapshot__isnull=True).count()}

    Snapshot: {Snapshot.objects.count()}
    """)
