package com.magistr.app.controller;

import com.magistr.app.dto.ScheduleOverrideDto;
import com.magistr.app.dto.ScheduleOverrideAvailabilityDto;
import com.magistr.app.service.ScheduleConflictException;
import com.magistr.app.service.ScheduleOverrideService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;

class ScheduleOverrideControllerTest {

    private static final LocalDate SOURCE_DATE = LocalDate.of(2026, 9, 1);
    private static final LocalDate TARGET_DATE = LocalDate.of(2026, 9, 10);

    private ScheduleOverrideService service;
    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        service = mock(ScheduleOverrideService.class);
        mockMvc = standaloneSetup(new ScheduleOverrideController(service))
                .setControllerAdvice(new GlobalExceptionHandler())
                .build();
    }

    @Test
    void returnsConflictWithRussianMessageForScheduleConflict() throws Exception {
        when(service.create(any(ScheduleOverrideDto.class))).thenThrow(new ScheduleConflictException(
                "Конфликт расписания: преподаватель уже занят в указанное время"
        ));

        mockMvc.perform(post("/api/edu-office/schedule/overrides")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {
                                  "baseRuleSlotId": 101,
                                  "lessonDate": "2026-09-01",
                                  "action": "MOVE",
                                  "newTimeSlotId": 2
                                }
                                """))
                .andExpect(status().isConflict())
                .andExpect(jsonPath("$.status").value(409))
                .andExpect(jsonPath("$.error").value("Конфликт"))
                .andExpect(jsonPath("$.message")
                        .value("Конфликт расписания: преподаватель уже занят в указанное время"));
    }

    @Test
    void returnsRegistryBySourceOrTargetRangeWithTargetDate() throws Exception {
        ScheduleOverrideDto override = new ScheduleOverrideDto(
                70L, 101L, SOURCE_DATE, TARGET_DATE, "MOVE", 2L,
                null, null, null, "Перенос", 5L, null
        );
        when(service.getAll(SOURCE_DATE, TARGET_DATE)).thenReturn(List.of(override));

        mockMvc.perform(get("/api/edu-office/schedule/overrides")
                        .param("startDate", SOURCE_DATE.toString())
                        .param("endDate", TARGET_DATE.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].lessonDate[0]").value(2026))
                .andExpect(jsonPath("$[0].lessonDate[1]").value(9))
                .andExpect(jsonPath("$[0].lessonDate[2]").value(1))
                .andExpect(jsonPath("$[0].targetLessonDate[2]").value(10));

        verify(service).getAll(SOURCE_DATE, TARGET_DATE);
    }

    @Test
    void returnsSemesterAvailabilityForSourceOccurrence() throws Exception {
        when(service.getAvailability(101L, SOURCE_DATE)).thenReturn(new ScheduleOverrideAvailabilityDto(
                8L,
                LocalDate.of(2026, 9, 1),
                LocalDate.of(2027, 1, 31),
                List.of(SOURCE_DATE, TARGET_DATE)
        ));

        mockMvc.perform(get("/api/edu-office/schedule/overrides/availability")
                        .param("baseRuleSlotId", "101")
                        .param("lessonDate", SOURCE_DATE.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.semesterId").value(8))
                .andExpect(jsonPath("$.availableDates[1][0]").value(2026))
                .andExpect(jsonPath("$.availableDates[1][1]").value(9))
                .andExpect(jsonPath("$.availableDates[1][2]").value(10));
    }
}
