from enum import Enum

from django.db import models
from django.db.models import UniqueConstraint
from slugify import slugify
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.fields import RichTextField, StreamField
from wagtail.models import Page

from vbv_lernwelt.core.constants import (
    DEFAULT_RICH_TEXT_FEATURES,
    DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER,
)
from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseBasePage


class AssignmentListPage(CourseBasePage):
    subpage_types = ["assignment.Assignment"]
    parent_page_types = ["course.CoursePage"]

    def save(self, clean=True, user=None, log_action=False, **kwargs):
        self.slug = find_available_slug(
            slugify(f"{self.get_parent().slug}-assignment", allow_unicode=True),
            ignore_page_id=self.id,
        )
        super(AssignmentListPage, self).save(clean, user, log_action, **kwargs)

    def __str__(self):
        return f"{self.title}"


class ExplanationBlock(blocks.StructBlock):
    text = blocks.RichTextBlock(features=DEFAULT_RICH_TEXT_FEATURES)

    class Meta:
        icon = "comment"


class PerformanceObjectiveBlock(blocks.StructBlock):
    text = blocks.TextBlock()

    class Meta:
        icon = "tick"


class UserTextInputBlock(blocks.StructBlock):
    text = blocks.RichTextBlock(
        blank=True, required=False, features=DEFAULT_RICH_TEXT_FEATURES
    )

    class Meta:
        icon = "edit"


class UserConfirmationBlock(blocks.StructBlock):
    text = blocks.RichTextBlock(features=DEFAULT_RICH_TEXT_FEATURES)

    class Meta:
        icon = "tick-inverse"


class TaskContentStreamBlock(blocks.StreamBlock):
    explanation = ExplanationBlock()
    user_text_input = UserTextInputBlock()
    user_confirmation = UserConfirmationBlock()


class TaskBlock(blocks.StructBlock):
    title = blocks.TextBlock()
    file_submission_required = blocks.BooleanBlock(required=False)
    content = TaskContentStreamBlock(
        blank=True,
    )

    class Meta:
        icon = "tasks"
        label = "Teilauftrag"


class EvaluationSubTaskBlock(blocks.StructBlock):
    title = blocks.TextBlock()
    description = blocks.RichTextBlock(
        blank=True, required=False, features=DEFAULT_RICH_TEXT_FEATURES
    )
    points = blocks.IntegerBlock()

    class Meta:
        icon = "tick"
        label = "Beurteilung"


class EvaluationTaskBlock(blocks.StructBlock):
    title = blocks.TextBlock()
    description = blocks.RichTextBlock(
        blank=True, required=False, features=DEFAULT_RICH_TEXT_FEATURES
    )
    max_points = blocks.IntegerBlock()
    sub_tasks = blocks.ListBlock(
        EvaluationSubTaskBlock(), blank=True, use_json_field=True
    )

    class Meta:
        icon = "tasks"
        label = "Beurteilungskriterium"


AssignmentType = Enum(
    "AssignmentType",
    [
        "CASEWORK",  # Geleitete Fallarbeit
        "PREP_ASSIGNMENT",  # Vorbereitungsauftrag
        "REFLECTION",  # Reflexion
    ],
)


class Assignment(CourseBasePage):
    serialize_field_names = [
        "intro_text",
        "effort_required",
        "performance_objectives",
        "evaluation_description",
        "evaluation_document_url",
        "tasks",
        "evaluation_tasks",
    ]

    assignment_type = models.CharField(
        max_length=50,
        choices=[(tag.name, tag.name) for tag in AssignmentType],
        default=AssignmentType.CASEWORK.name,
    )

    intro_text = RichTextField(
        help_text="Erläuterung der Ausgangslage",
        features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER,
    )
    effort_required = models.CharField(
        max_length=100, help_text="Zeitaufwand als Text", blank=True
    )

    performance_objectives = StreamField(
        [
            ("performance_objective", PerformanceObjectiveBlock()),
        ],
        use_json_field=True,
        blank=True,
        help_text="Leistungsziele des Auftrags",
    )

    tasks = StreamField(
        [
            ("task", TaskBlock()),
        ],
        use_json_field=True,
        blank=True,
        help_text="Teilaufgaben",
    )

    evaluation_description = RichTextField(
        blank=True,
        help_text="Beschreibung der Bewertung",
        features=DEFAULT_RICH_TEXT_FEATURES,
    )
    evaluation_document_url = models.CharField(
        max_length=255,
        blank=True,
        help_text="URL zum Beurteilungsinstrument",
    )

    evaluation_tasks = StreamField(
        [
            ("task", EvaluationTaskBlock()),
        ],
        use_json_field=True,
        blank=True,
        help_text="Beurteilungsschritte",
    )

    content_panels = Page.content_panels + [
        FieldPanel("assignment_type"),
        FieldPanel("intro_text"),
        FieldPanel("effort_required"),
        FieldPanel("performance_objectives"),
        FieldPanel("tasks"),
        FieldPanel("evaluation_description"),
        FieldPanel("evaluation_document_url"),
        FieldPanel("evaluation_tasks"),
    ]

    subpage_types = []

    class Meta:
        verbose_name = "Auftrag"

    def save(self, clean=True, user=None, log_action=False, **kwargs):
        self.slug = find_available_slug(
            slugify(f"{self.get_parent().slug}-{self.title}", allow_unicode=True),
            ignore_page_id=self.id,
        )
        super(Assignment, self).save(clean, user, log_action, **kwargs)

    def filter_user_subtasks(self, subtask_types=None):
        """
        Filters out all the subtasks which require user input
        :param subtask_types:
        :return: list of subtasks with the shape: [
          {
            "id": "<uuid>",
            "type": "user_confirmation",
            "value": {
              "text": "Ja, ich habe Motorfahrzeugversicherungspolice..."
            }
          },
          {
            "id": "<uuid>",
            "type": "user_text_input",
            "value": {
              "text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen..."
          },
        ]
        """
        if subtask_types is None:
            subtask_types = ["user_text_input", "user_confirmation"]

        raw_tasks = self.tasks.raw_data

        return [
            sub_dict
            for task_dict in raw_tasks
            for sub_dict in task_dict["value"]["content"]
            if sub_dict["type"] in subtask_types
        ]

    def get_evaluation_tasks(self):
        return [task for task in self.evaluation_tasks.raw_data]

    def get_input_tasks(self):
        return self.filter_user_subtasks() + self.get_evaluation_tasks()


AssignmentCompletionStatus = Enum(
    "AssignmentCompletionStatus",
    ["IN_PROGRESS", "SUBMITTED", "EVALUATION_IN_PROGRESS", "EVALUATION_SUBMITTED"],
)


def is_valid_assignment_completion_status(status):
    return status in AssignmentCompletionStatus.__members__


class AssignmentCompletion(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    submitted_at = models.DateTimeField(null=True, blank=True)
    evaluation_submitted_at = models.DateTimeField(null=True, blank=True)
    evaluation_user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="+",
    )
    evaluation_grade = models.FloatField(null=True, blank=True)
    evaluation_points = models.FloatField(null=True, blank=True)

    assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
    assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
    course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)

    completion_status = models.CharField(
        max_length=255,
        choices=[(acs.name, acs.name) for acs in AssignmentCompletionStatus],
        default="IN_PROGRESS",
    )

    completion_data = models.JSONField(default=dict)
    additional_json_data = models.JSONField(default=dict)

    class Meta:
        constraints = [
            UniqueConstraint(
                fields=["assignment_user", "assignment", "course_session"],
                name="assignment_completion_unique_user_assignment_course_session",
            )
        ]


class AssignmentCompletionAuditLog(models.Model):
    """
    This model is used to store the "SUBMITTED" and "EVALUATION_SUBMITTED" data separately
    """

    created_at = models.DateTimeField(auto_now_add=True)

    evaluation_user = models.ForeignKey(
        User, on_delete=models.SET_NULL, null=True, blank=True, related_name="+"
    )
    assignment_user = models.ForeignKey(
        User, on_delete=models.SET_NULL, null=True, related_name="+"
    )
    assignment = models.ForeignKey(
        Assignment, on_delete=models.SET_NULL, null=True, related_name="+"
    )
    course_session = models.ForeignKey(
        "course.CourseSession", on_delete=models.SET_NULL, null=True, related_name="+"
    )

    completion_status = models.CharField(
        max_length=255,
        choices=[(acs.value, acs.name) for acs in AssignmentCompletionStatus],
        default="IN_PROGRESS",
    )

    completion_data = models.JSONField(default=dict)
    additional_json_data = models.JSONField(default=dict)

    assignment_user_email = models.CharField(max_length=255)
    assignment_slug = models.CharField(max_length=255)
    evaluation_user_email = models.CharField(max_length=255, blank=True, default="")
    evaluation_grade = models.FloatField(null=True, blank=True)
    evaluation_points = models.FloatField(null=True, blank=True)
