from django.db import models
from django.db.models import UniqueConstraint
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from django_jsonform.models.fields import JSONField
from grapple.models import GraphQLString
from wagtail.models import Page

from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
from vbv_lernwelt.files.models import UploadFile


class Course(models.Model):
    title = models.CharField(_("Titel"), max_length=255)
    category_name = models.CharField(
        _("Kategorie-Name"), max_length=255, default="Kategorie"
    )
    slug = models.SlugField(
        _("Slug"), max_length=255, unique=True, blank=True, allow_unicode=True
    )

    class Meta:
        verbose_name = _("Lehrgang")

    def get_course_url(self):
        return f"/course/{self.slug}"

    def get_learning_path(self):
        from vbv_lernwelt.learnpath.models import LearningPath

        return self.coursepage.get_children().exact_type(LearningPath).first().specific

    def get_learning_path_url(self):
        return self.get_learning_path().get_frontend_url()

    def get_cockpit_url(self):
        return self.get_learning_path().get_cockpit_url()

    def get_competence_url(self):
        from vbv_lernwelt.competence.models import CompetenceProfilePage

        competence_page = (
            self.coursepage.get_children().exact_type(CompetenceProfilePage).first()
        )
        return competence_page.specific.get_frontend_url()

    def get_media_library_url(self):
        from vbv_lernwelt.media_library.models import MediaLibraryPage

        media_library_page = (
            self.coursepage.get_children().exact_type(MediaLibraryPage).first()
        )
        return media_library_page.specific.get_frontend_url()

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


class CourseCategory(models.Model):
    # Die Handlungsfelder im "Versicherungsvermittler/in"
    title = models.CharField(_("Titel"), max_length=255, blank=True)
    course = models.ForeignKey("course.Course", on_delete=models.CASCADE)
    general = models.BooleanField(_("Allgemein"), default=False)

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


class CourseBasePage(Page):
    class Meta:
        abstract = True

    serialize_field_names = []
    serialize_base_field_names = [
        "id",
        "title",
        "slug",
        "content_type",
        "translation_key",
        "frontend_url",
    ]

    graphql_fields = [
        GraphQLString(
            field_name="frontend_url",
            source="get_graphql_frontend_url",
        )
    ]

    def get_graphql_frontend_url(self, values):
        return self.get_frontend_url()

    def get_course_parent(self):
        return self.get_ancestors(inclusive=True).exact_type(CoursePage).last()

    def get_course(self):
        course_parent_page = self.get_course_parent()
        if course_parent_page:
            return course_parent_page.specific.course
        return None

    def get_circles(self):
        course_parent_page = self.get_course_parent()

        if course_parent_page:
            from vbv_lernwelt.learnpath.models import Circle, LearningPath

            circles = (
                course_parent_page.get_children()
                .exact_type(LearningPath)
                .first()
                .get_children()
                .exact_type(Circle)
            )
            return circles

        return None

    @classmethod
    def get_serializer_class(cls):
        return get_course_serializer_class(
            cls,
            field_names=cls.serialize_field_names,
            base_field_names=cls.serialize_base_field_names,
        )

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


class CoursePage(CourseBasePage):
    content_panels = Page.content_panels
    subpage_types = [
        "learnpath.LearningPath",
        "competence.CompetenceProfilePage",
        "media_library.MediaLibraryPage",
        "assignment.AssignmentListPage",
    ]
    course = models.OneToOneField("course.Course", on_delete=models.PROTECT)

    class Meta:
        verbose_name = _("Lehrgang-Seite")

    def save(self, *args, **kwargs):
        self.slug = find_available_slug(
            slugify(self.title, allow_unicode=True), ignore_page_id=self.id
        )
        super(CoursePage, self).save(*args, **kwargs)

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


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

    user = models.ForeignKey(User, on_delete=models.CASCADE)

    # page can logically be a LearningContent or a PerformanceCriteria for now
    page_key = models.UUIDField()
    page_type = models.CharField(max_length=255, default="", blank=True)
    page_slug = models.CharField(max_length=255, default="", blank=True)

    course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)

    completion_status = models.CharField(
        max_length=255,
        choices=[
            ("unknown", "unknown"),
            ("success", "success"),
            ("fail", "fail"),
        ],
        default="unknown",
    )
    additional_json_data = models.JSONField(default=dict)

    class Meta:
        constraints = [
            UniqueConstraint(
                fields=["user", "page_key", "course_session"],
                name="course_completion_unique_user_page_key",
            )
        ]


class CourseSession(models.Model):
    """
    Die Durchführung eines Kurses
    Benutzer die an eine CourseSession gehängt sind können diesen Lehrgang sehen
    Das anhängen kann via CourseSessionUser oder "Schulklasse (TODO)" geschehen
    """

    ATTENDANCE_COURSES_SCHEMA = {
        "type": "array",
        "items": {
            "type": "object",
            "properties": {
                "learningContentId": {
                    "type": "number",
                    "title": "ID des Lerninhalts",
                    "required": True,
                },
                "start": {"type": "string", "format": "datetime"},
                "end": {"type": "string", "format": "datetime"},
                "location": {"type": "string"},
                "trainer": {"type": "string"},
            },
        },
    }

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    course = models.ForeignKey("course.Course", on_delete=models.CASCADE)
    title = models.TextField(unique=True)

    import_id = models.TextField(blank=True, default="")

    generation = models.TextField(blank=True, default="")
    region = models.TextField(blank=True, default="")
    group = models.TextField(blank=True, default="")

    start_date = models.DateField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)

    attendance_courses = JSONField(
        schema=ATTENDANCE_COURSES_SCHEMA, blank=True, default=list
    )
    assignment_details_list = models.JSONField(default=list, blank=True)

    additional_json_data = models.JSONField(default=dict, blank=True)

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


class CourseSessionUser(models.Model):
    """
    Ein Benutzer der an einer CourseSession teilnimmt
    """

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

    class Role(models.TextChoices):
        MEMBER = "MEMBER", _("Teilnehmer")
        EXPERT = "EXPERT", _("Experte/Trainer")
        TUTOR = "TUTOR", _("Lernbegleitung")

    role = models.CharField(choices=Role.choices, max_length=255, default=Role.MEMBER)

    expert = models.ManyToManyField(
        "learnpath.Circle", related_name="expert", blank=True
    )

    class Meta:
        constraints = [
            UniqueConstraint(
                fields=[
                    "course_session",
                    "user",
                ],
                name="course_session_user_unique_course_session_user",
            )
        ]

    def to_dict(self):
        return {
            "session_id": self.course_session.id,
            "session_title": self.course_session.title,
            "user_id": self.user.id,
            "first_name": self.user.first_name,
            "last_name": self.user.last_name,
            "email": self.user.email,
            "avatar_url": self.user.avatar_url,
            "role": self.role,
            "circles": self.expert.all().values(
                "id", "title", "slug", "translation_key"
            ),
        }


class CircleDocument(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    file = models.OneToOneField(UploadFile, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)

    course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
    learning_sequence = models.ForeignKey(
        "learnpath.LearningSequence", on_delete=models.CASCADE
    )

    @property
    def url(self) -> str:
        return self.file.url

    @property
    def file_name(self) -> str:
        return self.file.original_file_name

    def delete(self, *args, **kwargs):
        self.file.upload_finished_at = None
        self.file.save()
        return super().delete(*args, **kwargs)
