from typing import List, Tuple

from model_utils.models import TimeStampedModel, UUIDModel

from django.db import models
from django.db.models import Prefetch
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

from lms.classrooms.models import StudentSlot
from lms.courses.models import Course
from lms.enrollments.models import EnrolledUser
from lms.tracker.models import EnrollmentTrackerIssue


class ReportFile(TimeStampedModel, UUIDModel):
    class TypeChoices(models.TextChoices):
        ENROLLED_USERS_REPORT = 'enrolled_users_report', _("отчет: список заявок на курс")
        STUDENT_SLOTS_REPORT = (
            'student_slots_report',
            _("отчет: список заявок на слоты курса")
        )

    class StatusChoices(models.TextChoices):
        CREATED = 'created', _("создан")
        COMPLETED = 'completed', _("завершен")
        ERROR = 'error', _("ошибка")

    SUBMODEL_MAP = {
        TypeChoices.ENROLLED_USERS_REPORT: 'enrolledusersreport',
        TypeChoices.STUDENT_SLOTS_REPORT: 'studentslotsreport',
    }

    def content_upload_path(instance, filename):  # noqa: N805
        """
        Отдельный метод, который можно перегрузить при наследовании
        Возвращает путь к отчету
        """
        return f'lms/reports/default/{filename}'

    def _content_upload_path(instance, filename):  # noqa: N805
        return instance.content_upload_path(filename)

    def get_report_type(self):
        raise NotImplementedError(
            '''
            To use reports you need to implement `get_report_type` in inherited class
            to get one of `TypeChoices`
            '''
        )

    def get_report(self):
        return getattr(self, ReportFile.SUBMODEL_MAP[self.report_type])

    def get_report_data(self):
        return self.get_report().report_data

    @cached_property
    def report_data(self) -> Tuple[List[str], Tuple[Tuple]]:
        raise NotImplementedError(
            '''
            To use reports you need to implement `report_data` in inherited class
            '''
        )

    report_type = models.CharField(
        verbose_name=_("тип отчета"),
        max_length=255,
        choices=TypeChoices.choices,
        editable=False,
    )

    status = models.CharField(
        _("статус операции"),
        max_length=16,
        choices=StatusChoices.choices,
        default=StatusChoices.CREATED,
    )
    error_message = models.TextField(_("сообщение об ошибке"), blank=True)

    file = models.FileField(_("файл"), upload_to=_content_upload_path, blank=True)

    class Meta:
        ordering = ('-created',)
        verbose_name = _("файл с отчетом")
        verbose_name_plural = _("файлы с отчетами")

    def __str__(self):
        return f'{self.id} | {self.get_report_type_display()}| {self.status}'

    def save(self, *args, **kwargs):
        if not self.report_type:
            self.report_type = self.get_report_type()

        return super().save(*args, **kwargs)


class EnrolledUsersReport(ReportFile):
    def content_upload_path(instance, filename):  # noqa: N805
        return f'lms/reports/enrolled_users/{filename}'

    def get_report_type(self):
        return self.TypeChoices.ENROLLED_USERS_REPORT

    course = models.ForeignKey(
        Course,
        verbose_name=_("Курс"),
        on_delete=models.CASCADE,
        related_name='enrolled_users_reports',
    )

    class Meta(ReportFile.Meta):
        verbose_name = _("отчет: заявки на курс")
        verbose_name_plural = _("отчеты: заявки на курс")

    @cached_property
    def report_data(self) -> Tuple[List[str], Tuple[Tuple]]:
        queryset = (
            EnrolledUser.objects.filter(course=self.course)
            .select_related('course', 'group', 'enrollment', 'user', 'user__staffprofile')
            .prefetch_related(
                Prefetch(
                    lookup='enrollment_tracker_issues',
                    queryset=EnrollmentTrackerIssue.objects.all().select_related('queue'),
                ),
            )
        )

        array = (
            (
                row.id,
                row.course_id,
                row.course.slug,
                row.enrollment.get_enroll_type_display(),
                row.user.username,
                row.group_id,
                row.group.name if row.group else None,
                row.user.staffprofile.groups_str(start=0, end=1),
                row.user.staffprofile.groups_str(start=1, end=2),
                row.user.staffprofile.groups_str(start=2, end=3),
                row.user.staffprofile.groups_str(start=3),
                '\n'.join([issue.issue for issue in row.enrollment_tracker_issues.all()]),
                row.enroll_date.replace(tzinfo=None) if row.enroll_date else None,
                row.completion_date.replace(tzinfo=None) if row.completion_date else None,
                row.get_status_display(),
                row.created.replace(tzinfo=None) if row.created else None,
                row.group.begin_date.replace(tzinfo=None) if row.group and row.group.begin_date else None,
                row.group.end_date.replace(tzinfo=None) if row.group and row.group.end_date else None,
            ) for row in queryset
        )

        colnames = [
            _("ID заявки"),
            _("Слаг курса"),
            _("Название курса"),
            _("Метода зачисления"),
            _("Логин"),
            _("ID группы"),
            _("Название группы"),
            _("Уровень 1"),
            _("Уровень 2"),
            _("Уровень 3"),
            _("Остальные уровни"),
            _("Тикеты"),
            _("Дата зачисления"),
            _("Дата завершения"),
            _("Статус заявки"),
            _("Дата создания заявки"),
            _("Начало обучения"),
            _("Окончание обучения"),
        ]

        return colnames, array


class StudentSlotsReport(ReportFile):
    def content_upload_path(instance, filename):  # noqa: N805
        return f'lms/reports/student_slots/{filename}'

    def get_report_type(self):
        return self.TypeChoices.STUDENT_SLOTS_REPORT

    course = models.ForeignKey(
        Course,
        verbose_name=_("Курс"),
        on_delete=models.CASCADE,
        related_name='student_slots_reports',
    )

    class Meta(ReportFile.Meta):
        verbose_name = _("отчет: заявки на слоты курса")
        verbose_name_plural = _("отчеты: заявки на слоты курса")

    @cached_property
    def report_data(self) -> Tuple[List[str], Tuple[Tuple]]:
        queryset = (
            StudentSlot.objects.filter(timeslot__course=self.course)
            .select_related('student', 'student__group', 'timeslot', 'timeslot__course', 'timeslot__classroom')
            .prefetch_related('issues')
        )

        array = (
            (
                row.id,
                row.timeslot.course_id,
                row.timeslot.course.slug,
                row.timeslot.course.name,
                row.timeslot.classroom.name,
                str(row.timeslot),
                row.student.user.username,
                row.student.group_id,
                row.student.group.name if row.student.group else None,
                row.student.user.staffprofile.groups_str(start=0, end=1),
                row.student.user.staffprofile.groups_str(start=1, end=2),
                row.student.user.staffprofile.groups_str(start=2, end=3),
                row.student.user.staffprofile.groups_str(start=3),
                '\n'.join([issue.issue for issue in row.issues.all()]),
                row.get_status_display(),
                row.created.replace(tzinfo=None) if row.created else None,
            ) for row in queryset
        )

        colnames = [
            _("ID заявки"),
            _("ID курса"),
            _("Слаг курса"),
            _("Название курса"),
            _("Название модуля"),
            _("Слот"),
            _("Логин"),
            _("ID группы"),
            _("Название группы"),
            _("Уровень 1"),
            _("Уровень 2"),
            _("Уровень 3"),
            _("Остальные уровни"),
            _("Тикеты"),
            _("Статус заявки"),
            _("Дата создания заявки"),
        ]

        return colnames, array
