package com.magistr.app.service;

import com.magistr.app.dto.ScheduleRuleDto;
import com.magistr.app.dto.ScheduleRuleSlotDto;
import com.magistr.app.model.AcademicYear;
import com.magistr.app.model.Classroom;
import com.magistr.app.model.LessonType;
import com.magistr.app.model.LifecycleEntity;
import com.magistr.app.model.Role;
import com.magistr.app.model.ScheduleParity;
import com.magistr.app.model.ScheduleRule;
import com.magistr.app.model.ScheduleRuleSlot;
import com.magistr.app.model.Semester;
import com.magistr.app.model.SemesterType;
import com.magistr.app.model.StudentGroup;
import com.magistr.app.model.Subgroup;
import com.magistr.app.model.Subject;
import com.magistr.app.model.TimeSlot;
import com.magistr.app.model.TimeSlotScope;
import com.magistr.app.model.User;
import com.magistr.app.repository.ClassroomRepository;
import com.magistr.app.repository.GroupRepository;
import com.magistr.app.repository.LessonTypesRepository;
import com.magistr.app.repository.ScheduleRuleRepository;
import com.magistr.app.repository.SemesterRepository;
import com.magistr.app.repository.SubjectRepository;
import com.magistr.app.repository.SubgroupRepository;
import com.magistr.app.repository.TimeSlotRepository;
import com.magistr.app.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.test.util.ReflectionTestUtils;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class ScheduleRuleServiceTest {

    private static final long RULE_ID = 100L;
    private static final long SUBJECT_ID = 10L;
    private static final long SEMESTER_ID = 20L;
    private static final long GROUP_ID = 30L;
    private static final long TIME_SLOT_ID = 40L;
    private static final long TEACHER_ID = 50L;
    private static final long OTHER_TEACHER_ID = 51L;
    private static final long CLASSROOM_ID = 60L;
    private static final long OTHER_CLASSROOM_ID = 61L;
    private static final long LECTURE_TYPE_ID = 70L;
    private static final long LABORATORY_TYPE_ID = 71L;
    private static final long SUBGROUP_A_ID = 80L;
    private static final long SUBGROUP_B_ID = 81L;

    @Mock
    private ScheduleRuleRepository scheduleRuleRepository;
    @Mock
    private SubjectRepository subjectRepository;
    @Mock
    private SemesterRepository semesterRepository;
    @Mock
    private GroupRepository groupRepository;
    @Mock
    private TimeSlotRepository timeSlotRepository;
    @Mock
    private UserRepository userRepository;
    @Mock
    private ClassroomRepository classroomRepository;
    @Mock
    private LessonTypesRepository lessonTypesRepository;
    @Mock
    private SubgroupRepository subgroupRepository;
    @Mock
    private ScheduleGeneratorService scheduleGeneratorService;

    private ScheduleRuleService service;
    private Subject subject;
    private Semester semester;
    private StudentGroup group;
    private TimeSlot timeSlot;
    private User teacher;
    private User otherTeacher;
    private Classroom classroom;
    private Classroom otherClassroom;
    private LessonType lectureType;
    private LessonType laboratoryType;
    private Subgroup subgroupA;
    private Subgroup subgroupB;

    @BeforeEach
    void setUp() {
        subject = subject();
        semester = semester();
        group = group();
        timeSlot = timeSlot();
        teacher = user(TEACHER_ID, Role.TEACHER, "Первый преподаватель");
        otherTeacher = user(OTHER_TEACHER_ID, Role.TEACHER, "Второй преподаватель");
        classroom = classroom(CLASSROOM_ID, "А-101");
        otherClassroom = classroom(OTHER_CLASSROOM_ID, "Б-202");
        lectureType = lessonType(LECTURE_TYPE_ID, "Лекция");
        laboratoryType = lessonType(LABORATORY_TYPE_ID, "Лабораторная работа");
        subgroupA = subgroup(SUBGROUP_A_ID, "Подгруппа 1");
        subgroupB = subgroup(SUBGROUP_B_ID, "Подгруппа 2");

        service = new ScheduleRuleService(
                scheduleRuleRepository,
                subjectRepository,
                semesterRepository,
                groupRepository,
                timeSlotRepository,
                userRepository,
                classroomRepository,
                lessonTypesRepository,
                subgroupRepository,
                scheduleGeneratorService
        );

        when(semesterRepository.findByIdForUpdate(SEMESTER_ID)).thenReturn(Optional.of(semester));
        when(semesterRepository.findAllByIdForUpdateOrderById(List.of(SEMESTER_ID)))
                .thenReturn(List.of(semester));
        when(subjectRepository.findById(SUBJECT_ID)).thenReturn(Optional.of(subject));
        when(groupRepository.findAllById(List.of(GROUP_ID))).thenReturn(List.of(group));
        when(timeSlotRepository.findById(TIME_SLOT_ID)).thenReturn(Optional.of(timeSlot));
        when(userRepository.findById(TEACHER_ID)).thenReturn(Optional.of(teacher));
        when(userRepository.findById(OTHER_TEACHER_ID)).thenReturn(Optional.of(otherTeacher));
        when(classroomRepository.findById(CLASSROOM_ID)).thenReturn(Optional.of(classroom));
        when(classroomRepository.findById(OTHER_CLASSROOM_ID)).thenReturn(Optional.of(otherClassroom));
        when(lessonTypesRepository.findById(LECTURE_TYPE_ID)).thenReturn(Optional.of(lectureType));
        when(lessonTypesRepository.findById(LABORATORY_TYPE_ID)).thenReturn(Optional.of(laboratoryType));
        when(subgroupRepository.findById(SUBGROUP_A_ID)).thenReturn(Optional.of(subgroupA));
        when(subgroupRepository.findById(SUBGROUP_B_ID)).thenReturn(Optional.of(subgroupB));
        when(scheduleRuleRepository.findActiveBySemesterIdWithDetails(SEMESTER_ID, null))
                .thenReturn(List.of());
        when(scheduleRuleRepository.saveAndFlush(any(ScheduleRule.class)))
                .thenAnswer(invocation -> assignGeneratedIds(invocation.getArgument(0)));
    }

    @Test
    void rejectsOddHoursOfEveryLessonCategoryWithRussianMessages() {
        assertThatThrownBy(() -> service.create(ruleRequest(1, 0, 0, lectureSlot())))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Количество часов для лекций должно быть чётным");

        assertThatThrownBy(() -> service.create(ruleRequest(0, 3, 0, laboratorySlot(SUBGROUP_A_ID))))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Количество часов для лабораторных занятий должно быть чётным");

        assertThatThrownBy(() -> service.create(ruleRequest(0, 0, 5, lectureSlot())))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Количество часов для практических занятий должно быть чётным");

        verify(semesterRepository, never()).findByIdForUpdate(any());
    }

    @Test
    void validatesMissingNegativeZeroAndUnusedTypeHours() {
        assertThatThrownBy(() -> service.create(ruleRequest(null, 0, 0, lectureSlot())))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Укажите количество часов для лекций");

        assertThatThrownBy(() -> service.create(ruleRequest(-2, 0, 0, lectureSlot())))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Количество часов для лекций не может быть отрицательным");

        assertThatThrownBy(() -> service.create(ruleRequest(0, 0, 0, lectureSlot())))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Укажите часы хотя бы для одного типа занятий");

        ScheduleRuleDto saved = service.create(ruleRequest(2, 0, 0, lectureSlot()));
        assertThat(saved.lectureAcademicHours()).isEqualTo(2);
        assertThat(saved.laboratoryAcademicHours()).isZero();
        assertThat(saved.practiceAcademicHours()).isZero();
    }

    @Test
    void rejectsActiveUserWithoutTeacherRole() {
        User administrator = user(TEACHER_ID, Role.ADMIN, "Администратор");
        when(userRepository.findById(TEACHER_ID)).thenReturn(Optional.of(administrator));

        assertThatThrownBy(() -> service.create(ruleRequest(2, 0, 0, lectureSlot())))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("В слот расписания можно назначить только пользователя с ролью преподавателя");

        verify(scheduleRuleRepository, never()).saveAndFlush(any());
    }

    @Test
    void treatsMissingReferencedTeacherAsInvalidPayload() {
        when(userRepository.findById(TEACHER_ID)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.create(ruleRequest(2, 0, 0, lectureSlot())))
                .isExactlyInstanceOf(IllegalArgumentException.class)
                .hasMessage("Преподаватель не найден");

        verify(scheduleRuleRepository, never()).saveAndFlush(any());
    }

    @Test
    void rejectsUnsupportedFormatAndAcceptsTrimmedSupportedFormat() {
        ScheduleRuleSlotDto invalid = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Гибрид"
        );
        assertThatThrownBy(() -> service.create(ruleRequest(2, 0, 0, invalid)))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Формат занятия должен быть «Очно» или «Онлайн»");

        ScheduleRuleSlotDto valid = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), " Онлайн "
        );
        ScheduleRuleDto saved = service.create(ruleRequest(2, 0, 0, valid));

        assertThat(saved.slots()).singleElement().extracting(ScheduleRuleSlotDto::lessonFormat)
                .isEqualTo("Онлайн");
    }

    @Test
    void rejectsExactDuplicateInsideNewRule() {
        ScheduleRuleSlotDto duplicate = lectureSlot();

        assertThatThrownBy(() -> service.create(ruleRequest(2, 0, 0, duplicate, duplicate)))
                .isInstanceOfSatisfying(ScheduleRuleConflictException.class, exception -> {
                    assertThat(exception.getMessage())
                            .isEqualTo("Невозможно сохранить правило: в списке есть дублирующиеся слоты");
                    assertThat(exception.getConflictRule()).isNull();
                    assertThat(exception.getConflictFields()).containsExactly("slot");
                    assertThat(exception.getConflictReasons()).containsExactly("Дублирующийся слот");
                });

        verify(scheduleRuleRepository, never()).saveAndFlush(any());
    }

    @Test
    void rejectsInternalConflictAndReportsAllSharedResourcesInStableOrder() {
        ScheduleRuleSlotDto first = lectureSlot();
        ScheduleRuleSlotDto second = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Онлайн"
        );

        assertThatThrownBy(() -> service.create(ruleRequest(4, 0, 0, first, second)))
                .isInstanceOfSatisfying(ScheduleRuleConflictException.class, exception -> {
                    assertThat(exception.getMessage())
                            .isEqualTo("Невозможно сохранить правило: слоты внутри правила конфликтуют");
                    assertThat(exception.getConflictRule()).isNull();
                    assertThat(exception.getConflictFields()).containsExactly("teacher", "classroom", "group");
                    assertThat(exception.getConflictReasons())
                            .containsExactly("Преподаватель", "Аудитория", "Группа");
                });
    }

    @Test
    void allowsOddAndEvenSlotsBecauseTheirActiveWeeksDoNotOverlap() {
        ScheduleRuleSlotDto odd = slot(
                ScheduleParity.ODD, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Очно"
        );
        ScheduleRuleSlotDto even = slot(
                ScheduleParity.EVEN, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Очно"
        );

        ScheduleRuleDto saved = service.create(ruleRequest(4, 0, 0, odd, even));

        assertThat(saved.slots()).extracting(ScheduleRuleSlotDto::parity)
                .containsExactlyInAnyOrder(ScheduleParity.ODD, ScheduleParity.EVEN);
    }

    @Test
    void rejectsBothParityWhenItOverlapsOddOrEvenSlot() {
        ScheduleRuleSlotDto bothFirst = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Очно"
        );
        ScheduleRuleSlotDto oddSecond = slot(
                ScheduleParity.ODD, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Онлайн"
        );
        assertThatThrownBy(() -> service.create(ruleRequest(6, 0, 0, bothFirst, oddSecond)))
                .isInstanceOf(ScheduleRuleConflictException.class)
                .hasMessage("Невозможно сохранить правило: слоты внутри правила конфликтуют");

        ScheduleRuleSlotDto evenFirst = slot(
                ScheduleParity.EVEN, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Очно"
        );
        ScheduleRuleSlotDto bothSecond = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Онлайн"
        );
        assertThatThrownBy(() -> service.create(ruleRequest(6, 0, 0, evenFirst, bothSecond)))
                .isInstanceOf(ScheduleRuleConflictException.class)
                .hasMessage("Невозможно сохранить правило: слоты внутри правила конфликтуют");
    }

    @Test
    void rejectsWholeGroupAndSubgroupAtSameTime() {
        ScheduleRuleSlotDto wholeGroup = laboratorySlot();
        ScheduleRuleSlotDto subgroup = slot(
                ScheduleParity.BOTH, OTHER_TEACHER_ID, OTHER_CLASSROOM_ID,
                LABORATORY_TYPE_ID, List.of(SUBGROUP_A_ID), "Очно"
        );

        assertThatThrownBy(() -> service.create(ruleRequest(0, 4, 0, wholeGroup, subgroup)))
                .isInstanceOfSatisfying(ScheduleRuleConflictException.class, exception -> {
                    assertThat(exception.getConflictFields()).containsExactly("group");
                    assertThat(exception.getConflictReasons()).containsExactly("Группа");
                });
    }

    @Test
    void rejectsSameSubgroupAtSameTime() {
        ScheduleRuleSlotDto first = laboratorySlot(SUBGROUP_A_ID);
        ScheduleRuleSlotDto second = slot(
                ScheduleParity.BOTH, OTHER_TEACHER_ID, OTHER_CLASSROOM_ID,
                LABORATORY_TYPE_ID, List.of(SUBGROUP_A_ID), "Онлайн"
        );

        assertThatThrownBy(() -> service.create(ruleRequest(0, 4, 0, first, second)))
                .isInstanceOfSatisfying(ScheduleRuleConflictException.class, exception ->
                        assertThat(exception.getConflictFields()).containsExactly("group"));
    }

    @Test
    void allowsDifferentSubgroupsOfSameGroupAtSameTime() {
        ScheduleRuleSlotDto first = laboratorySlot(SUBGROUP_A_ID);
        ScheduleRuleSlotDto second = slot(
                ScheduleParity.BOTH, OTHER_TEACHER_ID, OTHER_CLASSROOM_ID,
                LABORATORY_TYPE_ID, List.of(SUBGROUP_B_ID), "Очно"
        );

        ScheduleRuleDto saved = service.create(ruleRequest(0, 4, 0, first, second));

        assertThat(saved.slots()).hasSize(2);
    }

    @Test
    void ignoresArchivedExternalRuleButRejectsEquivalentActiveRule() {
        ScheduleRule archived = persistedRule(200L, LifecycleEntity.STATUS_ARCHIVED, "Очно");
        when(scheduleRuleRepository.findActiveBySemesterIdWithDetails(SEMESTER_ID, null))
                .thenReturn(List.of(archived));

        ScheduleRuleDto saved = service.create(ruleRequest(2, 0, 0, lectureSlot()));
        assertThat(saved.id()).isNotNull();

        ScheduleRule active = persistedRule(201L, LifecycleEntity.STATUS_ACTIVE, "Очно");
        when(scheduleRuleRepository.findActiveBySemesterIdWithDetails(SEMESTER_ID, null))
                .thenReturn(List.of(active));

        assertThatThrownBy(() -> service.create(ruleRequest(2, 0, 0, lectureSlot())))
                .isInstanceOfSatisfying(ScheduleRuleConflictException.class, exception -> {
                    assertThat(exception.getMessage()).isEqualTo("Невозможно сохранить правило: слот занят");
                    assertThat(exception.getConflictRule()).isNotNull();
                    assertThat(exception.getConflictRule().id()).isEqualTo(201L);
                    assertThat(exception.getConflictFields()).containsExactly("teacher", "classroom", "group");
                });
    }

    @Test
    void updateExcludesCurrentRuleFromExternalConflictCheck() {
        ScheduleRule managed = persistedRule(RULE_ID, LifecycleEntity.STATUS_ACTIVE, "Очно");
        when(scheduleRuleRepository.findByIdForUpdate(RULE_ID)).thenReturn(Optional.of(managed));
        when(scheduleRuleRepository.findActiveBySemesterIdWithDetails(SEMESTER_ID, RULE_ID))
                .thenReturn(List.of(managed));
        ScheduleRuleSlotDto updatedSlot = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Онлайн"
        );

        ScheduleRuleDto updated = service.update(RULE_ID, ruleRequest(2, 0, 0, updatedSlot));

        assertThat(updated.id()).isEqualTo(RULE_ID);
        assertThat(updated.slots()).singleElement().extracting(ScheduleRuleSlotDto::lessonFormat)
                .isEqualTo("Онлайн");
        verify(scheduleRuleRepository).findActiveBySemesterIdWithDetails(SEMESTER_ID, RULE_ID);
    }

    @Test
    void rejectedUpdateDoesNotMutateManagedRule() {
        ScheduleRule managed = persistedRule(RULE_ID, LifecycleEntity.STATUS_ACTIVE, "Очно");
        ScheduleRule externalConflict = persistedRule(201L, LifecycleEntity.STATUS_ACTIVE, "Онлайн");
        when(scheduleRuleRepository.findByIdForUpdate(RULE_ID)).thenReturn(Optional.of(managed));
        when(scheduleRuleRepository.findActiveBySemesterIdWithDetails(SEMESTER_ID, RULE_ID))
                .thenReturn(List.of(externalConflict));
        ScheduleRuleSlotDto changedSlot = slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Онлайн"
        );

        assertThatThrownBy(() -> service.update(RULE_ID, ruleRequest(2, 0, 0, changedSlot)))
                .isInstanceOf(ScheduleRuleConflictException.class)
                .hasMessage("Невозможно сохранить правило: слот занят");

        assertThat(managed.getSlots()).singleElement().satisfies(slot ->
                assertThat(slot.getLessonFormat()).isEqualTo("Очно"));
        verify(scheduleRuleRepository, never()).saveAndFlush(managed);
    }

    private ScheduleRuleDto ruleRequest(Integer lectureHours,
                                        Integer laboratoryHours,
                                        Integer practiceHours,
                                        ScheduleRuleSlotDto... slots) {
        return new ScheduleRuleDto(
                null,
                SUBJECT_ID,
                null,
                SEMESTER_ID,
                null,
                null,
                lectureHours,
                laboratoryHours,
                practiceHours,
                1,
                1,
                1,
                List.of(GROUP_ID),
                null,
                List.of(slots)
        );
    }

    private ScheduleRuleSlotDto lectureSlot() {
        return slot(
                ScheduleParity.BOTH, TEACHER_ID, CLASSROOM_ID,
                LECTURE_TYPE_ID, List.of(), "Очно"
        );
    }

    private ScheduleRuleSlotDto laboratorySlot(Long... subgroupIds) {
        return slot(
                ScheduleParity.BOTH,
                TEACHER_ID,
                CLASSROOM_ID,
                LABORATORY_TYPE_ID,
                List.of(subgroupIds),
                "Очно"
        );
    }

    private ScheduleRuleSlotDto slot(ScheduleParity parity,
                                     long teacherId,
                                     long classroomId,
                                     long lessonTypeId,
                                     List<Long> subgroupIds,
                                     String format) {
        return new ScheduleRuleSlotDto(
                null,
                1,
                null,
                parity,
                TIME_SLOT_ID,
                null,
                null,
                null,
                null,
                subgroupIds,
                null,
                teacherId,
                null,
                classroomId,
                null,
                lessonTypeId,
                null,
                format
        );
    }

    private ScheduleRule persistedRule(long id, String status, String format) {
        ScheduleRule rule = new ScheduleRule();
        rule.setId(id);
        rule.setStatus(status);
        rule.setSubject(subject);
        rule.setSemester(semester);
        rule.setLectureAcademicHours(2);
        rule.setLaboratoryAcademicHours(0);
        rule.setPracticeAcademicHours(0);
        rule.setLectureStartWeek(1);
        rule.setLaboratoryStartWeek(1);
        rule.setPracticeStartWeek(1);
        rule.setGroups(new LinkedHashSet<>(List.of(group)));

        ScheduleRuleSlot slot = new ScheduleRuleSlot();
        ReflectionTestUtils.setField(slot, "id", id * 10);
        slot.setScheduleRule(rule);
        slot.setDayOfWeek(1);
        slot.setParity(ScheduleParity.BOTH);
        slot.setTimeSlot(timeSlot);
        slot.setTeacher(teacher);
        slot.setClassroom(classroom);
        slot.setLessonType(lectureType);
        slot.setLessonFormat(format);
        rule.setSlots(new LinkedHashSet<>(List.of(slot)));
        return rule;
    }

    private ScheduleRule assignGeneratedIds(ScheduleRule rule) {
        if (rule.getId() == null) {
            rule.setId(900L);
        }
        AtomicLong nextSlotId = new AtomicLong(1_000L);
        rule.getSlots().stream()
                .filter(slot -> slot.getId() == null)
                .forEach(slot -> ReflectionTestUtils.setField(slot, "id", nextSlotId.getAndIncrement()));
        return rule;
    }

    private Subject subject() {
        Subject subject = new Subject();
        subject.setId(SUBJECT_ID);
        subject.setName("Тестовая дисциплина");
        subject.setDepartmentId(1L);
        return subject;
    }

    private Semester semester() {
        AcademicYear year = new AcademicYear();
        year.setId(1L);
        year.setTitle("2026-2027");
        year.setStartDate(LocalDate.of(2026, 9, 1));
        year.setEndDate(LocalDate.of(2027, 6, 30));

        Semester semester = new Semester();
        semester.setId(SEMESTER_ID);
        semester.setAcademicYear(year);
        semester.setSemesterType(SemesterType.autumn);
        semester.setStartDate(LocalDate.of(2026, 9, 1));
        semester.setEndDate(LocalDate.of(2027, 1, 31));
        return semester;
    }

    private StudentGroup group() {
        StudentGroup group = new StudentGroup();
        group.setId(GROUP_ID);
        group.setName("Тестовая группа");
        return group;
    }

    private TimeSlot timeSlot() {
        TimeSlotScope scope = new TimeSlotScope();
        scope.setId(1L);
        scope.setApplyMode("DEFAULT");
        TimeSlot slot = new TimeSlot();
        slot.setId(TIME_SLOT_ID);
        slot.setOrderNumber(1);
        slot.setStartTime(LocalTime.of(9, 0));
        slot.setEndTime(LocalTime.of(10, 30));
        slot.setDurationMinutes(90);
        slot.setTimeSlotScope(scope);
        return slot;
    }

    private User user(long id, Role role, String name) {
        User user = new User();
        user.setId(id);
        user.setRole(role);
        user.setFullName(name);
        user.setJobTitle("Преподаватель");
        return user;
    }

    private Classroom classroom(long id, String name) {
        Classroom classroom = new Classroom();
        classroom.setId(id);
        classroom.setName(name);
        classroom.setIsAvailable(true);
        return classroom;
    }

    private LessonType lessonType(long id, String name) {
        LessonType type = new LessonType();
        type.setId(id);
        type.setLessonType(name);
        return type;
    }

    private Subgroup subgroup(long id, String name) {
        Subgroup subgroup = new Subgroup();
        subgroup.setId(id);
        subgroup.setName(name);
        subgroup.setStudentGroup(group);
        return subgroup;
    }
}
