import mimetypes
import uuid
from datetime import datetime, timedelta

from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.fields.files import FieldFile
from django.urls import reverse
from django.utils import timezone
from django.utils.safestring import mark_safe
from markdownx.models import MarkdownxField

from shared.models import NameStrMixin

from . import settings as app_settings

# Create your models here.


def get_file_location(instance, filename, path_prefix="public"):
    mimetypes_instance = mimetypes.MimeTypes()

    current_time = datetime.today()
    guessed_type = mimetypes_instance.guess_type(filename, strict=False)[0]

    extension = ""

    if guessed_type is not None:
        for mapper in mimetypes_instance.types_map_inv:
            if guessed_type not in mapper:
                continue

            extension = mapper[guessed_type]

            if isinstance(extension, list):
                extension = extension[0]

            break

    return (
        f"{path_prefix}/"
        f"{current_time.year}/{current_time.month}/{current_time.day}/"
        f"{uuid.uuid4()}{extension}"
    )


class LectureGroup(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=128,
        verbose_name="Jméno",
    )

    description = MarkdownxField(
        null=True,
        blank=True,
        verbose_name="Popis",
        help_text=mark_safe(
            r'Můžeš použít <a href="'
            r"https://cs.wikipedia.org/wiki/Markdown"
            r'#P%C5%99%C3%ADklad_u%C5%BEit%C3%AD"'
            r">Markdown</a>."
        ),
    )

    priority = models.IntegerField(
        verbose_name="Priorita",
        help_text="Čím nižší číslo, tím výš se skupina zobrazí.",
    )

    user_groups = models.ManyToManyField(
        Group,
        blank=True,
        verbose_name="Uživatelské skupiny",
        help_text="Pokud žádné nedefinuješ, školení ve skupině jsou dostupná všem.",
    )

    class Meta:
        verbose_name = "Výuková skupina"
        verbose_name_plural = "Výukové skupiny"
        ordering = ("priority", "name")


class LectureGroupType(models.Model):
    lecture = models.ForeignKey(
        "Lecture",
        on_delete=models.CASCADE,
        related_name="lecture_group_types",
        verbose_name="Školení",
    )
    group = models.ForeignKey(
        "LectureGroup",
        on_delete=models.CASCADE,
        related_name="lecture_group_types",
        verbose_name="Výuková skupina",
    )

    class TypeChoices(models.TextChoices):
        REQUIRED = "required", "Silně doporučené školení"
        RECOMMENDED = "recommended", "Doporučené školení"
        VOLUNTARY = "voluntary", "Dobrovolné školení"

    type = models.CharField(
        choices=TypeChoices.choices,
        max_length=11,
        verbose_name="Požadovanost",
    )

    def __str__(self) -> str:
        return f"{self.group} - {self.lecture}"

    class Meta:
        verbose_name = "Úroveň požadovanosti pro skupinu"
        verbose_name_plural = "Úroveň požadovanosti pro skupiny"


class LectureCategory(models.Model):
    name = models.CharField(
        max_length=32,
        verbose_name="Jméno"
    )

    def __str__(self) -> str:
        return self.name

    class Meta:
        verbose_name = "Kategorie školení"
        verbose_name_plural = verbose_name


class Lecture(NameStrMixin, models.Model):
    is_current_starting_treshold = timedelta(hours=8)
    is_current_ending_treshold = timedelta(days=60)

    timestamp = models.DateTimeField(
        verbose_name="Datum a čas konání",
        blank=True,
        null=True,
    )  # If undefined, assume this event was in the past

    category = models.ForeignKey(
        "LectureCategory",
        null=True,
        on_delete=models.SET_NULL,
        related_name="lectures",
        verbose_name="Kategorie",
    )

    name = models.CharField(
        max_length=128,
        verbose_name="Název",
    )

    description = MarkdownxField(
        blank=True,
        null=True,
        verbose_name="Popis",
        help_text=mark_safe(
            r'Můžeš použít <a href="'
            r"https://cs.wikipedia.org/wiki/Markdown"
            r'#P%C5%99%C3%ADklad_u%C5%BEit%C3%AD"'
            r">Markdown</a>."
        ),
    )

    lectors = models.ManyToManyField(
        "LectureLector", related_name="lectures", verbose_name="Lektoři"
    )

    rsvp_users = models.ManyToManyField(
        "users.User",
        blank=True,
        related_name="rsvp_lectures",
        verbose_name="Zaregistrovaní uživatelé",
    )

    # Settings

    settings = app_settings.LectureSettings("Nastavení")

    def get_current_group_type(self, request, group=None):
        if group is not None:
            return LectureGroupType.objects.filter(lecture=self, group=group).first()
        else:
            return LectureGroupType.objects.filter(
                lecture=self,
                group__in=(
                    LectureGroup.filter(
                        models.Q(user_groups__in=request.user.groups.all())
                        if not request.user.is_superuser
                        else models.Q(id__isnull=False)  # Always True
                    )
                    .distinct()
                    .all()
                ),
            ).first()

    class Meta:
        verbose_name = "Školení"
        verbose_name_plural = verbose_name
        ordering = ("-timestamp", "-name")


class LectureLector(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=128,
        verbose_name="Jméno",
    )

    description = MarkdownxField(
        max_length=512,
        blank=True,
        null=True,
        verbose_name="Popis",
        help_text=mark_safe(
            r'Můžeš použít <a href="'
            r"https://cs.wikipedia.org/wiki/Markdown"
            r'#P%C5%99%C3%ADklad_u%C5%BEit%C3%AD"'
            r">Markdown</a>."
        ),
    )

    url = models.URLField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Odkaz",
        help_text=mark_safe(
            'Běžně na <a href="https://lide.pirati.cz">aplikaci Lidé</a>.'
        ),
    )

    avatar = models.ImageField(
        blank=True,
        null=True,
        upload_to=get_file_location,
        verbose_name="Profilový obrázek",
        help_text=(
            "Vložený soubor má prioritu nad obrázkem synchronizovaným z Chobotnice."
        ),
    )

    username = models.CharField(
        max_length=128,
        blank=True,
        null=True,
        verbose_name="Uživatelské jméno",
        help_text=(
            "Např. na fóru, nebo v Chobotnici. Užívá se "
            "k synchronizaci profilového obrázku, v budoucnu "
            "i dalších informací."
        ),
    )

    class Meta:
        verbose_name = "Lektor"
        verbose_name_plural = "Lektoři"


class LectureRecording(NameStrMixin, models.Model):
    lecture = models.ForeignKey(
        "Lecture",
        on_delete=models.CASCADE,
        related_name="recordings",
        verbose_name="Školení",
    )

    name = models.CharField(
        max_length=128,
        verbose_name="Název",
    )

    link = models.URLField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Odkaz",
        help_text=mark_safe(
            'Běžně na <a href="https://tv.pirati.cz">Pirátskou TV</a>.'
        ),
    )

    class Meta:
        verbose_name = "Nahrávka"
        verbose_name_plural = "Nahrávky"


def get_lecture_material_file_location(instance, filename):
    return get_file_location(instance, filename, path_prefix="_private")


class LectureMaterialFileProxy(FieldFile):
    @property
    def url(self) -> str:
        return reverse("lectures:download_material_file", args=(str(self.instance.id),))


class LectureMaterialFileField(models.FileField):
    attr_class = LectureMaterialFileProxy


class LectureMaterial(NameStrMixin, models.Model):
    lecture = models.ForeignKey(
        "Lecture",
        on_delete=models.CASCADE,
        related_name="materials",
        verbose_name="Školení",
    )

    name = models.CharField(
        max_length=128,
        verbose_name="Název",
    )

    link = models.URLField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Odkaz",
        help_text="Pokud máš zadaný odkaz, nemůžeš definovat soubor.",
    )

    file = LectureMaterialFileField(
        blank=True,
        null=True,
        upload_to=get_lecture_material_file_location,
        verbose_name="Soubor",
        help_text="Pokud máš vložený soubor, nemůžeš definovat odkaz.",
    )

    @property
    def protected_file_url(self) -> str:
        return reverse(
            "lectures:download_material_file",
            args=(self.id,),
        )

    def clean(self) -> None:
        BOTH_FILE_AND_LINK_DEFINED = (
            "Definuj prosím pouze odkaz, nebo soubor. Nemůžeš mít oboje najednou."
        )
        NEITHER_FILE_NOR_LINK_DEFINED = (
            "Definuj prosím odkaz, nebo soubor. Aspoň jedna hodnota musí být vyplněna."
        )

        if not self.file and not self.link:
            raise ValidationError(
                {
                    "link": NEITHER_FILE_NOR_LINK_DEFINED,
                    "file": NEITHER_FILE_NOR_LINK_DEFINED,
                }
            )

        if self.file and self.link:
            raise ValidationError(
                {
                    "link": BOTH_FILE_AND_LINK_DEFINED,
                    "file": BOTH_FILE_AND_LINK_DEFINED,
                }
            )

    class Meta:
        verbose_name = "Materiál"
        verbose_name_plural = "Materiály"