# Celery задачи для schedule

from celery import shared_task
import logging
from django.utils import timezone
from datetime import timedelta
from django.db.models import Count, Q
from .models import Lesson, Subject, MentorSubject

logger = logging.getLogger(__name__)


@shared_task
def send_lesson_reminders():
    """
    Отправка напоминаний о предстоящих занятиях.
    
    Отправляет напоминания за:
    - 24 часа до занятия
    - 1 час до занятия
    - 15 минут до занятия
    
    Задача запускается каждые 15 минут через Celery Beat.
    """
    from apps.notifications.services import NotificationService
    
    now = timezone.now()
    sent_24h = 0
    sent_1h = 0
    sent_15m = 0
    
    try:
        # Находим все запланированные занятия, которые еще не начались и не отменены
        lessons = Lesson.objects.filter(
            start_time__gt=now,
            status='scheduled'
        ).select_related('client', 'client__user', 'mentor')
        
        # Напоминания за 24 часа (от 23:30 до 24:30)
        time_24h_min = now + timedelta(hours=23, minutes=30)
        time_24h_max = now + timedelta(hours=24, minutes=30)
        
        lessons_24h = lessons.filter(
            start_time__gte=time_24h_min,
            start_time__lte=time_24h_max,
            reminder_24h_sent=False
        )
        
        # Оптимизация: используем bulk_update вместо цикла с save()
        lessons_24h_list = list(lessons_24h)
        lessons_24h_to_update = []
        for lesson in lessons_24h_list:
            try:
                NotificationService.send_lesson_reminder(lesson, time_before="24 часа")
                lesson.reminder_24h_sent = True
                lessons_24h_to_update.append(lesson)
                sent_24h += 1
                logger.info(f'Отправлено напоминание за 24 часа для занятия {lesson.id}')
            except Exception as e:
                logger.error(f'Ошибка отправки напоминания за 24 часа для занятия {lesson.id}: {e}')
        if lessons_24h_to_update:
            Lesson.objects.bulk_update(lessons_24h_to_update, ['reminder_24h_sent'])
        
        # Напоминания за 1 час (от 50 минут до 70 минут)
        time_1h_min = now + timedelta(minutes=50)
        time_1h_max = now + timedelta(minutes=70)
        
        lessons_1h = lessons.filter(
            start_time__gte=time_1h_min,
            start_time__lte=time_1h_max,
            reminder_1h_sent=False
        )
        
        # Оптимизация: используем bulk_update вместо цикла с save()
        lessons_1h_list = list(lessons_1h)
        lessons_1h_to_update = []
        for lesson in lessons_1h_list:
            try:
                NotificationService.send_lesson_reminder(lesson, time_before="1 час")
                lesson.reminder_1h_sent = True
                lessons_1h_to_update.append(lesson)
                sent_1h += 1
                logger.info(f'Отправлено напоминание за 1 час для занятия {lesson.id}')
            except Exception as e:
                logger.error(f'Ошибка отправки напоминания за 1 час для занятия {lesson.id}: {e}')
        if lessons_1h_to_update:
            Lesson.objects.bulk_update(lessons_1h_to_update, ['reminder_1h_sent'])
        
        # Напоминания за 15 минут (от 10 минут до 20 минут)
        time_15m_min = now + timedelta(minutes=10)
        time_15m_max = now + timedelta(minutes=20)
        
        lessons_15m = lessons.filter(
            start_time__gte=time_15m_min,
            start_time__lte=time_15m_max,
            reminder_15m_sent=False
        )
        
        # Оптимизация: используем bulk_update вместо цикла с save()
        lessons_15m_list = list(lessons_15m)
        lessons_15m_to_update = []
        for lesson in lessons_15m_list:
            try:
                NotificationService.send_lesson_reminder(lesson, time_before="15 минут")
                lesson.reminder_15m_sent = True
                lessons_15m_to_update.append(lesson)
                sent_15m += 1
                logger.info(f'Отправлено напоминание за 15 минут для занятия {lesson.id}')
            except Exception as e:
                logger.error(f'Ошибка отправки напоминания за 15 минут для занятия {lesson.id}: {e}')
        if lessons_15m_to_update:
            Lesson.objects.bulk_update(lessons_15m_to_update, ['reminder_15m_sent'])
        
        total_sent = sent_24h + sent_1h + sent_15m
        logger.info(
            f'[send_lesson_reminders] Отправлено напоминаний: '
            f'24ч - {sent_24h}, 1ч - {sent_1h}, 15м - {sent_15m} (всего: {total_sent})'
        )
        
        return f'Отправлено: 24ч - {sent_24h}, 1ч - {sent_1h}, 15м - {sent_15m}'
        
    except Exception as e:
        logger.error(f'[send_lesson_reminders] Ошибка: {str(e)}', exc_info=True)
        raise


@shared_task
def send_attendance_confirmation_requests():
    """
    Отправка запросов о подтверждении присутствия за 3 часа до занятия.
    Проверяет все занятия, которые начинаются через 3 часа или меньше,
    и отправляет запрос студенту, если еще не отправлен.
    """
    from django.utils import timezone
    from datetime import timedelta
    from apps.notifications.services import NotificationService
    
    now = timezone.now()
    # Занятия, которые начинаются через 3 часа или меньше
    time_threshold = now + timedelta(hours=3)
    
    # Находим занятия, которые:
    # 1. Еще не начались (start_time > now)
    # 2. Начинаются через 3 часа или меньше (start_time <= time_threshold)
    # 3. Еще не отменены
    # 4. Запрос о присутствии еще не отправлен
    lessons = Lesson.objects.filter(
        start_time__gt=now,
        start_time__lte=time_threshold,
        status='scheduled',
        attendance_confirmation_sent=False
    ).select_related('client', 'client__user', 'mentor')
    
    sent_count = 0
    lessons_to_update = []
    
    for lesson in lessons:
        try:
            # Отправляем запрос
            NotificationService.send_attendance_confirmation_request(lesson)
            
            # Отмечаем что запрос отправлен (накапливаем для bulk_update)
            lesson.attendance_confirmation_sent = True
            lessons_to_update.append(lesson)
            sent_count += 1
            
            logger.info(f'Отправлен запрос о присутствии для занятия {lesson.id}')
        except Exception as e:
            logger.error(f'Ошибка отправки запроса о присутствии для занятия {lesson.id}: {e}')
    
    # Оптимизация: используем bulk_update вместо цикла с save()
    if lessons_to_update:
        Lesson.objects.bulk_update(lessons_to_update, ['attendance_confirmation_sent'], batch_size=100)
    
    logger.info(f'[send_attendance_confirmation_requests] Отправлено {sent_count} запросов о присутствии')
    return f'Отправлено {sent_count} запросов'


@shared_task
def maintain_recurring_lessons():
    """
    Поддержание 12 будущих занятий для повторяющихся занятий.
    
    Задача проверяет все повторяющиеся занятия и добавляет недостающие,
    чтобы всегда было 12 будущих занятий впереди.
    
    Запускается каждый день через Celery Beat.
    """
    now = timezone.now()
    added_count = 0
    
    try:
        # Находим все уникальные серии повторяющихся занятий
        recurring_series = Lesson.objects.filter(
            is_recurring=True,
            recurring_series_id__isnull=False
        ).values_list('recurring_series_id', flat=True).distinct()
        
        for series_id in recurring_series:
            # Находим все занятия этой серии, которые еще не прошли
            series_lessons = Lesson.objects.filter(
                recurring_series_id=series_id,
                start_time__gt=now  # Только будущие занятия
            ).order_by('start_time')
            
            if not series_lessons.exists():
                # Если нет будущих занятий, пропускаем эту серию
                continue
            
            # Находим последнее занятие в серии (самое дальнее по времени)
            last_lesson = series_lessons.last()
            
            # Подсчитываем, сколько будущих занятий есть
            future_count = series_lessons.count()
            
            # Если меньше 12, добавляем недостающие
            if future_count < 12:
                # Находим первое занятие серии для получения шаблона
                first_lesson = Lesson.objects.filter(
                    recurring_series_id=series_id,
                    parent_lesson__isnull=True  # Родительское занятие
                ).first()
                
                if not first_lesson:
                    # Если нет родительского, берем первое занятие серии
                    first_lesson = Lesson.objects.filter(
                        recurring_series_id=series_id
                    ).order_by('start_time').first()
                
                if not first_lesson:
                    continue
                
                # Получаем время начала и окончания последнего занятия
                last_start_time = last_lesson.start_time
                last_end_time = last_lesson.end_time
                duration_minutes = last_lesson.duration
                
                # Вычисляем, сколько занятий нужно добавить
                lessons_to_add = 12 - future_count
                
                # Создаем недостающие занятия
                new_lessons = []
                for i in range(1, lessons_to_add + 1):
                    # Каждое следующее занятие через неделю после предыдущего
                    new_start_time = last_start_time + timedelta(weeks=i)
                    new_end_time = new_start_time + timedelta(minutes=duration_minutes)
                    
                    lesson_data = {
                        'mentor': first_lesson.mentor,
                        'client': first_lesson.client,
                        'group': first_lesson.group,
                        'start_time': new_start_time,
                        'end_time': new_end_time,
                        'duration': duration_minutes,
                        'title': first_lesson.title,
                        'description': first_lesson.description or '',
                        'subject': first_lesson.subject,
                        'mentor_subject': first_lesson.mentor_subject,
                        'subject_name': first_lesson.subject_name or (first_lesson.subject.name if first_lesson.subject else '') or (first_lesson.mentor_subject.name if first_lesson.mentor_subject else ''),
                        'template': first_lesson.template,
                        'price': first_lesson.price,
                        'is_recurring': True,
                        'recurring_series_id': series_id,
                        'parent_lesson': first_lesson if first_lesson.parent_lesson is None else first_lesson.parent_lesson,
                    }
                    new_lessons.append(Lesson(**lesson_data))
                
                # Массовое создание для оптимизации
                if new_lessons:
                    Lesson.objects.bulk_create(new_lessons)
                    added_count += len(new_lessons)
                    logger.info(
                        f'Добавлено {len(new_lessons)} занятий для серии {series_id}. '
                        f'Теперь будущих занятий: {future_count + len(new_lessons)}'
                    )
        
        logger.info(f'[maintain_recurring_lessons] Добавлено {added_count} занятий для поддержания 12 будущих занятий')
        return f'Добавлено {added_count} занятий'
        
    except Exception as e:
        logger.error(f'[maintain_recurring_lessons] Ошибка: {str(e)}', exc_info=True)
        raise


@shared_task
def promote_mentor_subjects_to_subjects():
    """
    Переносит кастомные предметы менторов в общую модель Subject,
    если предмет используется более чем 10 менторами.
    
    Запускается периодически через Celery Beat (например, раз в день).
    """
    promoted_count = 0
    
    try:
        # Находим все уникальные названия кастомных предметов
        # и подсчитываем количество менторов, использующих каждый предмет
        from django.db.models import Count
        mentor_subjects_stats = MentorSubject.objects.values('name').annotate(
            mentor_count=Count('mentor', distinct=True)
        ).filter(mentor_count__gte=10)  # Используется 10+ менторами
        
        for stat in mentor_subjects_stats:
            subject_name = stat['name']
            mentor_count = stat['mentor_count']
            
            # Проверяем, существует ли уже такой предмет в Subject
            existing_subject = Subject.objects.filter(name__iexact=subject_name).first()
            
            if existing_subject:
                # Если предмет уже существует, просто активируем его
                if not existing_subject.is_active:
                    existing_subject.is_active = True
                    existing_subject.save()
                    logger.info(f'Активирован существующий предмет: {subject_name}')
            else:
                # Создаем новый предмет в Subject
                new_subject = Subject.objects.create(
                    name=subject_name,
                    is_active=True
                )
                logger.info(f'Создан новый предмет в общей модели: {subject_name} (используется {mentor_count} менторами)')
            
            # Обновляем все занятия, использующие этот кастомный предмет
            # Заменяем mentor_subject на subject
            mentor_subjects = MentorSubject.objects.filter(name__iexact=subject_name)
            
            for mentor_subject in mentor_subjects:
                # Находим или создаем Subject
                subject = Subject.objects.filter(name__iexact=subject_name).first()
                if not subject:
                    subject = Subject.objects.create(name=subject_name, is_active=True)
                
                # Обновляем занятия
                updated_lessons = Lesson.objects.filter(mentor_subject=mentor_subject).update(
                    subject=subject,
                    mentor_subject=None,
                    subject_name=subject.name
                )
                
                # Обновляем шаблоны
                from .models import LessonTemplate
                LessonTemplate.objects.filter(mentor_subject=mentor_subject).update(
                    subject=subject,
                    mentor_subject=None,
                    subject_name=subject.name
                )
                
                if updated_lessons > 0:
                    logger.info(
                        f'Обновлено {updated_lessons} занятий и шаблонов для предмета "{subject_name}" '
                        f'(ментор: {mentor_subject.mentor.get_full_name()})'
                    )
            
            # Удаляем кастомные предметы, которые были перенесены
            deleted_count = mentor_subjects.delete()[0]
            if deleted_count > 0:
                logger.info(f'Удалено {deleted_count} кастомных предметов "{subject_name}" после переноса в общую модель')
                promoted_count += deleted_count
        
        logger.info(f'[promote_mentor_subjects_to_subjects] Перенесено {promoted_count} кастомных предметов в общую модель')
        return f'Перенесено {promoted_count} предметов'
        
    except Exception as e:
        logger.error(f'[promote_mentor_subjects_to_subjects] Ошибка: {str(e)}', exc_info=True)
        raise


@shared_task(name='apps.schedule.tasks.start_lessons_automatically')
def start_lessons_automatically():
    """
    Автоматическое начало и завершение занятий по времени.
    
    Обновляет статус занятий:
    - 'scheduled' -> 'in_progress' когда наступает время начала (start_time <= now)
    - 'scheduled' или 'in_progress' -> 'completed' когда время окончания прошло (end_time < now)
    
    Запускается каждую минуту через Celery Beat.
    """
    now = timezone.now()
    started_count = 0
    completed_count = 0
    logger.info(f'[start_lessons_automatically] Запуск')

    try:
        # Находим все запланированные занятия, которые должны начаться
        # start_time <= now (время начала уже наступило)
        # end_time >= now (время окончания еще не наступило)
        # status = 'scheduled' (еще не начались)
        lessons_to_start = Lesson.objects.filter(
            status='scheduled',
            start_time__lte=now,
            end_time__gte=now
        ).select_related('mentor', 'client')
        
        # Оптимизация: используем bulk_update вместо цикла с save()
        lessons_to_start_list = list(lessons_to_start)
        for lesson in lessons_to_start_list:
            lesson.status = 'in_progress'
        if lessons_to_start_list:
            Lesson.objects.bulk_update(lessons_to_start_list, ['status'])
            started_count = len(lessons_to_start_list)
            for lesson in lessons_to_start_list:
                logger.info(f'Занятие {lesson.id} автоматически переведено в статус "in_progress"')
        
        # Находим занятия, которые уже прошли и должны быть завершены:
        # end_time < now - 15 минут (всегда ждём 15 мин после конца, даже если занятие создали задним числом)
        fifteen_minutes_ago = now - timedelta(minutes=15)
        lessons_to_complete = Lesson.objects.filter(
            status__in=['scheduled', 'in_progress'],
            end_time__lt=fifteen_minutes_ago
        ).select_related('mentor', 'client')

        lessons_to_complete_list = list(lessons_to_complete)
        if lessons_to_complete_list:
            logger.info(
                f'[start_lessons_automatically] Найдено {len(lessons_to_complete_list)} занятий '
                f'для завершения (end_time < {fifteen_minutes_ago})'
            )
        
        # Оптимизация: используем bulk_update вместо цикла с save()
        for lesson in lessons_to_complete_list:
            lesson.status = 'completed'
            lesson.completed_at = now
        if lessons_to_complete_list:
            Lesson.objects.bulk_update(lessons_to_complete_list, ['status', 'completed_at'])
            completed_count = len(lessons_to_complete_list)
            for lesson in lessons_to_complete_list:
                logger.info(f'Занятие {lesson.id} автоматически переведено в статус "completed" (время окончания прошло)')
                
                # Закрываем LiveKit комнату, если она есть
                try:
                    from apps.video.models import VideoRoom
                    from apps.video.services import get_sfu_client, SFUClientError
                    
                    video_room = VideoRoom.objects.filter(lesson=lesson).first()
                    if video_room and video_room.room_id:
                        sfu_client = get_sfu_client()
                        try:
                            sfu_client.delete_room(str(video_room.room_id))
                            logger.info(f'LiveKit комната {video_room.room_id} закрыта для урока {lesson.id}')
                        except SFUClientError as e:
                            logger.warning(f'Не удалось закрыть LiveKit комнату {video_room.room_id} для урока {lesson.id}: {e}')
                except Exception as e:
                    logger.error(f'Ошибка закрытия LiveKit комнаты для урока {lesson.id}: {str(e)}', exc_info=True)

                # Отправить ментору в Telegram сообщение с кнопками подтверждения
                # (только если кто-то не подключался — при обоих подключённых ментор подтверждает при выходе)
                try:
                    from apps.notifications.tasks import send_lesson_completion_confirmation_telegram
                    send_lesson_completion_confirmation_telegram.delay(lesson.id, only_if_someone_not_connected=True)
                except Exception as e:
                    logger.warning(f'Не удалось отправить подтверждение занятия в Telegram: {e}')
        
        if started_count > 0 or completed_count > 0:
            logger.info(f'[start_lessons_automatically] Начато: {started_count}, Завершено: {completed_count}')
        
        return f'Начато {started_count}, Завершено {completed_count}'
        
    except Exception as e:
        logger.error(f'[start_lessons_automatically] Ошибка: {str(e)}', exc_info=True)
        raise
