from datetime import timedelta

from django.core.validators import RegexValidator
from django.db import models
from django.db.models import Q
from django.utils import timezone


ACTIVE_BOOKING_STATUSES = ("new", "confirmed")


class ConsultationSlotQuerySet(models.QuerySet):
    def available(self):
        return (
            self.filter(is_active=True, start_at__gte=timezone.now())
            .exclude(bookings__status__in=ACTIVE_BOOKING_STATUSES)
            .distinct()
        )


class ConsultationSlot(models.Model):
    class Subject(models.TextChoices):
        ANY = "any", "Физика или математика"
        PHYSICS = "physics", "Физика"
        MATHEMATICS = "mathematics", "Математика"

    class Format(models.TextChoices):
        ONLINE = "online", "Онлайн"
        OFFLINE = "offline", "Очно"

    start_at = models.DateTimeField("Начало консультации", unique=True)
    duration_minutes = models.PositiveSmallIntegerField(
        "Длительность, минут",
        choices=((45, "45 минут"), (60, "60 минут"), (90, "90 минут"), (120, "120 минут")),
        default=60,
    )
    subject = models.CharField(
        "Доступный предмет", max_length=20, choices=Subject.choices, default=Subject.ANY
    )
    format = models.CharField("Формат", max_length=20, choices=Format.choices, default=Format.ONLINE)
    is_active = models.BooleanField("Открыт для записи", default=True)
    teacher_note = models.CharField("Заметка преподавателя", max_length=300, blank=True)
    created_at = models.DateTimeField("Создан", auto_now_add=True)

    objects = ConsultationSlotQuerySet.as_manager()

    class Meta:
        verbose_name = "Свободное время"
        verbose_name_plural = "Расписание"
        ordering = ("start_at",)

    def __str__(self):
        local_start = timezone.localtime(self.start_at)
        return f"{local_start:%d.%m.%Y, %H:%M} · {self.get_format_display()}"

    @property
    def end_at(self):
        return self.start_at + timedelta(minutes=self.duration_minutes)

    @property
    def is_available(self):
        return self.is_active and self.start_at >= timezone.now() and not self.bookings.filter(
            status__in=ACTIVE_BOOKING_STATUSES
        ).exists()


class ConsultationRequest(models.Model):
    class Subject(models.TextChoices):
        PHYSICS = "physics", "Физика"
        MATHEMATICS = "mathematics", "Математика"

    class Exam(models.TextChoices):
        OGE = "oge", "ОГЭ"
        EGE = "ege", "ЕГЭ"
        SCHOOL = "school", "Школьная программа"
        OTHER = "other", "Другая консультация"

    class Format(models.TextChoices):
        ONLINE = "online", "Онлайн"
        OFFLINE = "offline", "Очно"
        ANY = "any", "Не имеет значения"

    class Status(models.TextChoices):
        NEW = "new", "Новая"
        CONFIRMED = "confirmed", "Подтверждена"
        COMPLETED = "completed", "Проведена"
        CANCELLED = "cancelled", "Отменена"
        NO_SHOW = "no_show", "Не пришёл"

    name = models.CharField("Имя ученика или родителя", max_length=100)
    phone = models.CharField(
        "Телефон",
        max_length=30,
        validators=[
            RegexValidator(
                regex=r"^[0-9+()\-\s]{7,30}$",
                message="Введите телефон в привычном формате.",
            )
        ],
    )
    contact = models.CharField("Telegram или другой способ связи", max_length=100, blank=True)
    student_grade = models.PositiveSmallIntegerField(
        "Класс ученика", choices=tuple((grade, f"{grade} класс") for grade in range(5, 12)), blank=True, null=True
    )
    subject = models.CharField("Предмет", max_length=20, choices=Subject.choices)
    exam = models.CharField("Цель подготовки", max_length=20, choices=Exam.choices)
    format = models.CharField("Предпочтительный формат", max_length=20, choices=Format.choices, default=Format.ONLINE)
    slot = models.ForeignKey(
        ConsultationSlot,
        verbose_name="Выбранное время",
        related_name="bookings",
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )
    preferred_date = models.DateField("Дата консультации", blank=True, null=True)
    preferred_time = models.TimeField("Время консультации", blank=True, null=True)
    comment = models.TextField("Комментарий ученика", max_length=1000, blank=True)
    status = models.CharField("Статус", max_length=20, choices=Status.choices, default=Status.NEW)
    teacher_note = models.TextField("Заметка преподавателя", max_length=2000, blank=True)
    meeting_link = models.URLField("Ссылка на встречу", blank=True)
    cancellation_reason = models.CharField("Причина отмены", max_length=500, blank=True)
    reminder_sent = models.BooleanField("Напоминание отправлено", default=False)
    consent_given = models.BooleanField("Согласие на обработку данных", default=False)
    created_at = models.DateTimeField("Создана", auto_now_add=True)
    updated_at = models.DateTimeField("Изменена", auto_now=True)

    class Meta:
        verbose_name = "Запись на консультацию"
        verbose_name_plural = "Записи на консультации"
        ordering = ("preferred_date", "preferred_time", "-created_at")
        constraints = [
            models.UniqueConstraint(
                fields=("slot",),
                condition=Q(status__in=ACTIVE_BOOKING_STATUSES),
                name="unique_active_slot_booking",
            )
        ]

    def save(self, *args, **kwargs):
        if self.slot_id:
            local_start = timezone.localtime(self.slot.start_at)
            self.preferred_date = local_start.date()
            self.preferred_time = local_start.time()
            if self.format == self.Format.ANY:
                self.format = self.slot.format
        super().save(*args, **kwargs)

    def __str__(self):
        if self.preferred_date and self.preferred_time:
            return f"{self.name}: {self.get_subject_display()} — {self.preferred_date:%d.%m.%Y}, {self.preferred_time:%H:%M}"
        return f"{self.name}: {self.get_subject_display()}"
