package com.magistr.app.repository;

import com.magistr.app.model.ScheduleOverride;
import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public interface ScheduleOverrideRepository extends JpaRepository<ScheduleOverride, Long> {

    @Query("""
            select o
            from ScheduleOverride o
            left join fetch o.baseRuleSlot baseSlot
            left join fetch baseSlot.scheduleRule rule
            left join fetch rule.semester
            left join fetch o.newTimeSlot
            left join fetch o.newClassroom
            left join fetch o.newTeacher
            where o.lessonDate between :startDate and :endDate
               or o.targetLessonDate between :startDate and :endDate
            """)
    List<ScheduleOverride> findByLessonDateBetweenWithDetails(
            @Param("startDate") LocalDate startDate,
            @Param("endDate") LocalDate endDate
    );

    Optional<ScheduleOverride> findByBaseRuleSlotIdAndLessonDate(Long baseRuleSlotId, LocalDate lessonDate);

    @Query("""
            select distinct o
            from ScheduleOverride o
            join fetch o.baseRuleSlot baseSlot
            join fetch baseSlot.scheduleRule rule
            join fetch rule.subject
            left join fetch rule.groups
            join fetch rule.semester
            join fetch baseSlot.timeSlot
            join fetch baseSlot.teacher
            join fetch baseSlot.classroom
            join fetch baseSlot.lessonType
            left join fetch o.newTimeSlot
            left join fetch o.newClassroom
            left join fetch o.newTeacher
            order by o.lessonDate, o.id
            """)
    List<ScheduleOverride> findAllWithRegistryDetails();

    @Query("""
            select distinct o
            from ScheduleOverride o
            join fetch o.baseRuleSlot baseSlot
            join fetch baseSlot.scheduleRule rule
            join fetch rule.subject
            left join fetch rule.groups
            join fetch rule.semester
            join fetch baseSlot.timeSlot
            join fetch baseSlot.teacher
            join fetch baseSlot.classroom
            join fetch baseSlot.lessonType
            left join fetch o.newTimeSlot
            left join fetch o.newClassroom
            left join fetch o.newTeacher
            where o.lessonDate between :startDate and :endDate
               or o.targetLessonDate between :startDate and :endDate
            order by o.lessonDate, o.id
            """)
    List<ScheduleOverride> findForRegistry(
            @Param("startDate") LocalDate startDate,
            @Param("endDate") LocalDate endDate
    );

    @Query("""
            select o
            from ScheduleOverride o
            left join fetch o.baseRuleSlot baseSlot
            left join fetch baseSlot.scheduleRule rule
            left join fetch rule.semester
            left join fetch o.newTimeSlot
            left join fetch o.newClassroom
            left join fetch o.newTeacher
            where o.lessonDate in :dates
               or o.targetLessonDate in :dates
            """)
    List<ScheduleOverride> findAffectingDatesWithDetails(@Param("dates") Collection<LocalDate> dates);

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("select o from ScheduleOverride o where o.id = :id")
    Optional<ScheduleOverride> findByIdForUpdate(@Param("id") Long id);

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("""
            select o
            from ScheduleOverride o
            where o.baseRuleSlot.id = :baseRuleSlotId
              and o.lessonDate = :lessonDate
            """)
    Optional<ScheduleOverride> findByBaseRuleSlotIdAndLessonDateForUpdate(
            @Param("baseRuleSlotId") Long baseRuleSlotId,
            @Param("lessonDate") LocalDate lessonDate
    );
}
