package de.votian.services.service;

import de.votian.services.dto.PublicHolidayDayDto;
import de.votian.services.dto.PublicHolidayYearDto;
import de.votian.services.entity.PublicHoliday;
import de.votian.services.repository.PublicHolidayRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@SuppressWarnings("null")
class PublicHolidayServiceTest {

    private PublicHolidayRepository publicHolidayRepository;
    private PublicHolidayService publicHolidayService;

    @BeforeEach
    void setUp() {
        publicHolidayRepository = mock(PublicHolidayRepository.class);
        publicHolidayService = new PublicHolidayService(publicHolidayRepository);
    }

    @Test
    void getYearBuildsCalendarDaysAndYearSelectors() {
        int currentYear = LocalDate.now().getYear();
        PublicHoliday christmas = holiday(1L, currentYear + 1, 12, 25, "1. Weihnachtstag");

        when(publicHolidayRepository.findByHeadquartersIdAndYearValueOrderByMonthValueAscDayValueAscTimeFromAsc(1L, currentYear + 1))
                .thenReturn(List.of(christmas));
        when(publicHolidayRepository.findDistinctYearsByHeadquartersId(1L))
                .thenReturn(List.of(currentYear - 1, currentYear + 1));

        PublicHolidayYearDto result = publicHolidayService.getYear(1L, currentYear + 1);

        assertThat(result.getYear()).isEqualTo(currentYear + 1);
        assertThat(result.isEditable()).isTrue();
        assertThat(result.getAvailableYears()).contains(currentYear - 1, currentYear, currentYear + 1);
        assertThat(result.getCopySourceYears()).contains(currentYear - 1, currentYear);
        assertThat(result.getDays()).hasSize(LocalDate.of(currentYear + 1, 1, 1).lengthOfYear());
        assertThat(result.getDays().stream().filter(PublicHolidayDayDto::isHoliday).findFirst())
                .get()
                .extracting(PublicHolidayDayDto::getName)
                .isEqualTo("1. Weihnachtstag");
    }

    @Test
    void saveYearPreservesPastEntriesOfCurrentYearAndReplacesFutureEntries() {
        int currentYear = LocalDate.now().getYear();
        LocalDate today = LocalDate.now();
        LocalDate pastDate = today.minusDays(1);
        LocalDate futureDate = today.plusDays(10);

        PublicHoliday pastHoliday = holiday(1L, currentYear, pastDate.getMonthValue(), pastDate.getDayOfMonth(), "Vergangen");
        when(publicHolidayRepository.findByHeadquartersIdAndYearValueOrderByMonthValueAscDayValueAscTimeFromAsc(1L, currentYear))
                .thenReturn(List.of(pastHoliday))
                .thenReturn(List.of(pastHoliday));
        when(publicHolidayRepository.findDistinctYearsByHeadquartersId(1L))
                .thenReturn(List.of(currentYear));

        PublicHolidayDayDto futureHoliday = new PublicHolidayDayDto();
        futureHoliday.setMonth(futureDate.getMonthValue());
        futureHoliday.setDay(futureDate.getDayOfMonth());
        futureHoliday.setHoliday(true);
        futureHoliday.setName("Zukunft");

        PublicHolidayYearDto result = publicHolidayService.saveYear(1L, currentYear, List.of(futureHoliday));

        verify(publicHolidayRepository).deleteByHeadquartersIdAndYearValue(1L, currentYear);
        verify(publicHolidayRepository).saveAll(anyList());
        assertThat(result.getDays().stream()
                .filter(day -> day.getMonth().equals(pastDate.getMonthValue()) && day.getDay().equals(pastDate.getDayOfMonth()))
                .findFirst())
                .get()
                .extracting(PublicHolidayDayDto::isHoliday)
                .isEqualTo(true);
    }

    private PublicHoliday holiday(Long hqId, int year, int month, int day, String name) {
        PublicHoliday holiday = new PublicHoliday();
        holiday.setHeadquartersId(hqId);
        holiday.setYearValue(year);
        holiday.setMonthValue(month);
        holiday.setDayValue(day);
        holiday.setName(name);
        return holiday;
    }
}
