package com.magistr.app.service;

import com.magistr.app.model.StudentGroup;
import com.magistr.app.repository.GroupRepository;
import com.magistr.app.repository.ScheduleOverrideRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.IntStream;

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.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class ScheduleQueryServiceTest {

    @Test
    void rejectsTooBroadGroupSearch() {
        GroupRepository groupRepository = mock(GroupRepository.class);
        StudentGroupLifecycleService groupLifecycleService = mock(StudentGroupLifecycleService.class);
        when(groupRepository.findAll())
                .thenReturn(IntStream.rangeClosed(1, 51)
                        .mapToObj(this::group)
                        .toList());
        when(groupLifecycleService.mayHaveScheduleInRange(any(), any(), any())).thenReturn(true);
        ScheduleQueryService service = new ScheduleQueryService(
                mock(ScheduleGeneratorService.class),
                groupRepository,
                mock(ScheduleOverrideRepository.class),
                groupLifecycleService
        );

        assertThatThrownBy(() -> service.search(null, null, null, null,
                null, null, null, null,
                LocalDate.of(2026, 5, 1),
                LocalDate.of(2026, 5, 7)))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("Уточните группу или кафедру");
    }

    @Test
    void teacherSearchDoesNotLoadAllGroups() {
        ScheduleGeneratorService generatorService = mock(ScheduleGeneratorService.class);
        GroupRepository groupRepository = mock(GroupRepository.class);
        ScheduleOverrideRepository overrideRepository = mock(ScheduleOverrideRepository.class);
        StudentGroupLifecycleService groupLifecycleService = mock(StudentGroupLifecycleService.class);
        ScheduleQueryService service = new ScheduleQueryService(
                generatorService,
                groupRepository,
                overrideRepository,
                groupLifecycleService
        );
        LocalDate startDate = LocalDate.of(2026, 5, 1);
        LocalDate endDate = LocalDate.of(2026, 5, 7);
        when(generatorService.buildScheduleForTeacher(10L, startDate, endDate)).thenReturn(List.of());
        when(overrideRepository.findByLessonDateBetweenWithDetails(startDate, endDate)).thenReturn(List.of());

        service.search(null, 10L, null, null,
                null, null, null, null,
                startDate,
                endDate);

        verify(generatorService).buildScheduleForTeacher(10L, startDate, endDate);
        verify(groupRepository, never()).findAll();
    }

    @Test
    void rangeLimitCountsBothBoundaryDates() {
        ScheduleGeneratorService generatorService = mock(ScheduleGeneratorService.class);
        GroupRepository groupRepository = mock(GroupRepository.class);
        ScheduleOverrideRepository overrideRepository = mock(ScheduleOverrideRepository.class);
        StudentGroupLifecycleService groupLifecycleService = mock(StudentGroupLifecycleService.class);
        ScheduleQueryService service = new ScheduleQueryService(
                generatorService,
                groupRepository,
                overrideRepository,
                groupLifecycleService
        );
        LocalDate startDate = LocalDate.of(2026, 1, 1);
        LocalDate allowedEndDate = startDate.plusDays(119);
        LocalDate rejectedEndDate = startDate.plusDays(120);
        when(generatorService.buildScheduleForTeacher(10L, startDate, allowedEndDate))
                .thenReturn(List.of());
        when(overrideRepository.findByLessonDateBetweenWithDetails(startDate, allowedEndDate))
                .thenReturn(List.of());

        assertThat(service.search(null, 10L, null, null,
                null, null, null, null,
                startDate, allowedEndDate)).isEmpty();

        assertThatThrownBy(() -> service.search(null, 10L, null, null,
                null, null, null, null,
                startDate, rejectedEndDate))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Диапазон расписания не может превышать 120 дней");
    }

    @ParameterizedTest
    @ValueSource(ints = {51, 100})
    void aggregationSearchProcessesAllActiveGroupsWithoutInteractiveLimit(int groupCount) {
        ScheduleGeneratorService generatorService = mock(ScheduleGeneratorService.class);
        GroupRepository groupRepository = mock(GroupRepository.class);
        ScheduleOverrideRepository overrideRepository = mock(ScheduleOverrideRepository.class);
        StudentGroupLifecycleService groupLifecycleService = mock(StudentGroupLifecycleService.class);
        LocalDate startDate = LocalDate.of(2026, 5, 1);
        LocalDate endDate = LocalDate.of(2026, 5, 7);
        when(groupRepository.findAll())
                .thenReturn(IntStream.rangeClosed(1, groupCount)
                        .mapToObj(this::group)
                        .toList());
        when(groupLifecycleService.mayHaveScheduleInRange(any(), any(), any())).thenReturn(true);
        when(generatorService.buildScheduleForGroups(any(), any(), any())).thenReturn(List.of());
        when(overrideRepository.findByLessonDateBetweenWithDetails(startDate, endDate)).thenReturn(List.of());
        ScheduleQueryService service = new ScheduleQueryService(
                generatorService,
                groupRepository,
                overrideRepository,
                groupLifecycleService
        );

        var result = service.searchForAggregation(null, null, startDate, endDate);

        assertThat(result).isEmpty();
        verify(generatorService).buildScheduleForGroups(
                argThat(groups -> groups.size() == groupCount),
                eq(startDate),
                eq(endDate)
        );
        verify(overrideRepository).findByLessonDateBetweenWithDetails(startDate, endDate);
    }

    private StudentGroup group(int id) {
        StudentGroup group = new StudentGroup();
        group.setId((long) id);
        group.setName("Группа " + id);
        return group;
    }
}
