package com.magistr.app.repository;

import com.magistr.app.model.TeacherDepartmentAssignment;
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.List;
import java.util.Optional;
import java.util.Set;

public interface TeacherDepartmentAssignmentRepository extends JpaRepository<TeacherDepartmentAssignment, Long> {

    @Query("""
            select a
            from TeacherDepartmentAssignment a
            join fetch a.teacher
            join fetch a.department
            where a.teacher.id = :teacherId
            order by a.validFrom desc, a.id desc
            """)
    List<TeacherDepartmentAssignment> findHistoryByTeacherId(@Param("teacherId") Long teacherId);

    @Query("""
            select a
            from TeacherDepartmentAssignment a
            join fetch a.teacher
            join fetch a.department
            where a.teacher.id = :teacherId
              and a.primaryAssignment = true
              and a.validTo is null
            """)
    Optional<TeacherDepartmentAssignment> findOpenPrimaryByTeacherId(@Param("teacherId") Long teacherId);

    @Query("""
            select a
            from TeacherDepartmentAssignment a
            join fetch a.teacher
            join fetch a.department
            where a.department.id = :departmentId
              and a.validFrom <= :date
              and (a.validTo is null or a.validTo >= :date)
            order by a.teacher.fullName
            """)
    List<TeacherDepartmentAssignment> findDepartmentTeachersAtDate(
            @Param("departmentId") Long departmentId,
            @Param("date") LocalDate date
    );

    @Query("""
            select count(a) > 0
            from TeacherDepartmentAssignment a
            where a.teacher.id = :teacherId
              and a.department.id = :departmentId
              and a.validFrom <= :date
              and (a.validTo is null or a.validTo >= :date)
            """)
    boolean existsActiveAssignment(
            @Param("teacherId") Long teacherId,
            @Param("departmentId") Long departmentId,
            @Param("date") LocalDate date
    );

    @Query("""
            select a
            from TeacherDepartmentAssignment a
            join fetch a.department
            where a.teacher.id = :teacherId
              and a.primaryAssignment = true
              and a.validFrom <= :date
              and (a.validTo is null or a.validTo >= :date)
            order by a.validFrom desc, a.id desc
            """)
    Optional<TeacherDepartmentAssignment> findPrimaryAtDate(
            @Param("teacherId") Long teacherId,
            @Param("date") LocalDate date
    );

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("""
            select a
            from TeacherDepartmentAssignment a
            join fetch a.department
            where a.teacher.id = :teacherId
              and a.primaryAssignment = true
            order by a.validFrom, a.id
            """)
    List<TeacherDepartmentAssignment> findPrimaryHistoryForUpdate(@Param("teacherId") Long teacherId);

    @Query("""
            select a
            from TeacherDepartmentAssignment a
            join fetch a.teacher
            join fetch a.department
            where a.teacher.id in :teacherIds
              and a.validFrom <= :endDate
              and (a.validTo is null or a.validTo >= :startDate)
            order by a.teacher.id, a.validFrom, a.id
            """)
    List<TeacherDepartmentAssignment> findForTeachersBetween(
            @Param("teacherIds") Set<Long> teacherIds,
            @Param("startDate") LocalDate startDate,
            @Param("endDate") LocalDate endDate
    );
}
