package com.magistr.app.repository;

import com.magistr.app.model.Semester;
import com.magistr.app.model.SemesterType;
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 SemesterRepository extends JpaRepository<Semester, Long> {

    Optional<Semester> findFirstByStartDateLessThanEqualAndEndDateGreaterThanEqual(LocalDate startDate, LocalDate endDate);

    @Query("""
            select semester
            from Semester semester
            join fetch semester.academicYear
            where semester.startDate <= :endDate
              and semester.endDate >= :startDate
            order by semester.startDate, semester.id
            """)
    List<Semester> findOverlappingForSchedule(@Param("startDate") LocalDate startDate,
                                              @Param("endDate") LocalDate endDate);

    List<Semester> findByAcademicYearIdOrderByStartDateAsc(Long academicYearId);

    Optional<Semester> findByAcademicYearIdAndSemesterType(Long academicYearId, SemesterType semesterType);

    boolean existsByAcademicYearIdAndSemesterType(Long academicYearId, SemesterType semesterType);

    boolean existsByAcademicYearIdAndSemesterTypeAndIdNot(Long academicYearId,
                                                          SemesterType semesterType,
                                                          Long id);

    @Query("""
            select case when count(semester) > 0 then true else false end
            from Semester semester
            where semester.academicYear.id = :academicYearId
              and semester.startDate <= :endDate
              and semester.endDate >= :startDate
            """)
    boolean existsOverlapping(@Param("academicYearId") Long academicYearId,
                              @Param("startDate") LocalDate startDate,
                              @Param("endDate") LocalDate endDate);

    @Query("""
            select case when count(semester) > 0 then true else false end
            from Semester semester
            where semester.academicYear.id = :academicYearId
              and semester.id <> :excludedId
              and semester.startDate <= :endDate
              and semester.endDate >= :startDate
            """)
    boolean existsOverlappingExcluding(@Param("academicYearId") Long academicYearId,
                                       @Param("excludedId") Long excludedId,
                                       @Param("startDate") LocalDate startDate,
                                       @Param("endDate") LocalDate endDate);

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

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("""
            select semester
            from Semester semester
            where semester.id in :ids
            order by semester.id
            """)
    List<Semester> findAllByIdForUpdateOrderById(@Param("ids") Collection<Long> ids);
}
