package de.votian.services.service;

import de.votian.services.config.ParameterKeys;
import de.votian.services.dto.UserSessionDto;
import de.votian.services.entity.GroupwareAppointment;
import de.votian.services.repository.AppGroupRepository;
import de.votian.services.repository.CompanyRepository;
import de.votian.services.repository.CustomerRepository;
import de.votian.services.repository.EmployeeRepository;
import de.votian.services.repository.GroupwareAppointmentRepository;
import de.votian.services.repository.HeadquartersRepository;
import de.votian.services.repository.MetaTypeRepository;
import de.votian.services.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.server.ResponseStatusException;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

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.anyIterable;
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;

@SuppressWarnings("null")
class GroupwareServiceTest {

    private GroupwareAppointmentRepository appointmentRepository;
    private UserRepository userRepository;
    private EmployeeRepository employeeRepository;
    private HeadquartersRepository headquartersRepository;
    private AppGroupRepository appGroupRepository;
    private MetaTypeRepository metaTypeRepository;
    private CustomerRepository customerRepository;
    private CompanyRepository companyRepository;
    private ParameterService parameterService;
    private JdbcTemplate jdbcTemplate;
    private ObjectProvider<JavaMailSender> mailSenderProvider;
    private GroupwareService groupwareService;

    @BeforeEach
    void setUp() {
        appointmentRepository = mock(GroupwareAppointmentRepository.class);
        userRepository = mock(UserRepository.class);
        employeeRepository = mock(EmployeeRepository.class);
        headquartersRepository = mock(HeadquartersRepository.class);
        appGroupRepository = mock(AppGroupRepository.class);
        metaTypeRepository = mock(MetaTypeRepository.class);
        customerRepository = mock(CustomerRepository.class);
        companyRepository = mock(CompanyRepository.class);
        parameterService = mock(ParameterService.class);
        jdbcTemplate = mock(JdbcTemplate.class);
        @SuppressWarnings("unchecked")
        ObjectProvider<JavaMailSender> provider = mock(ObjectProvider.class);
        mailSenderProvider = provider;

        when(mailSenderProvider.getIfAvailable()).thenReturn(null);
        when(metaTypeRepository.findByTypeOrderBySort(any())).thenReturn(List.of());
        when(userRepository.findAllById(anyIterable())).thenReturn(List.of());
        when(headquartersRepository.findAllById(anyIterable())).thenReturn(List.of());
        when(customerRepository.findAllById(anyIterable())).thenReturn(List.of());
        when(companyRepository.findAllById(anyIterable())).thenReturn(List.of());

        groupwareService = new GroupwareService(
                appointmentRepository,
                userRepository,
                employeeRepository,
                headquartersRepository,
                appGroupRepository,
                metaTypeRepository,
                customerRepository,
                companyRepository,
                parameterService,
                jdbcTemplate,
                mailSenderProvider
        );
    }

    @Test
    void confirmAppointmentAddsCurrentUserOnce() {
        GroupwareAppointment appointment = new GroupwareAppointment();
        appointment.setId(15L);
        appointment.setHeadquartersId(1L);
        appointment.setUserId(7L);
        appointment.setConfirmed(",7,");
        appointment.setExecDate(LocalDateTime.of(2026, 3, 28, 10, 0));
        appointment.setEndDate(LocalDateTime.of(2026, 3, 28, 11, 0));

        when(appointmentRepository.findByIdAndHeadquartersId(15L, 1L)).thenReturn(Optional.of(appointment));
        when(appointmentRepository.save(any(GroupwareAppointment.class))).thenAnswer(invocation -> invocation.getArgument(0));

        UserSessionDto user = sessionUser(12L, 1L, 5L);

        groupwareService.confirmAppointment(user, 15L);

        assertThat(appointment.getConfirmed()).isEqualTo(",7,12,");
        verify(appointmentRepository).save(appointment);
    }

    @Test
    void finishAppointmentRequiresSameDayReportWhenConfigured() {
        GroupwareAppointment appointment = new GroupwareAppointment();
        appointment.setId(9L);
        appointment.setHeadquartersId(1L);
        appointment.setUserId(7L);
        appointment.setCustomerId(44L);
        appointment.setCategory1(1);
        appointment.setCategory4(1);
        appointment.setExecDate(LocalDateTime.of(2026, 3, 28, 8, 0));
        appointment.setEndDate(LocalDateTime.of(2026, 3, 28, 9, 0));

        when(appointmentRepository.findByIdAndHeadquartersId(9L, 1L)).thenReturn(Optional.of(appointment));
        when(parameterService.getParameterValueFullFallback(5L,
                ParameterKeys.MASK_AP_WARNING_DISABLE_QUESTION_NO_REPORT_OF_SAME_DAY,
                1L)).thenReturn("1");
        when(jdbcTemplate.queryForObject(any(String.class), eq(Integer.class), eq(44L), any(LocalDateTime.class), any(LocalDateTime.class)))
                .thenReturn(0);

        UserSessionDto user = sessionUser(12L, 1L, 5L);

        assertThatThrownBy(() -> groupwareService.finishAppointment(user, 9L))
                .isInstanceOf(ResponseStatusException.class)
                .hasMessageContaining("Bericht");

        verify(appointmentRepository, never()).save(any(GroupwareAppointment.class));
    }

    @Test
    void parseAndStoreUserIdsKeepLegacyCommaFormat() {
        assertThat(groupwareService.parseUserIds(",5,8,5,")).containsExactly(5L, 8L);
        assertThat(groupwareService.parseUserIds("5,8")).containsExactly(5L, 8L);
        assertThat(groupwareService.storeUserIds(List.of(5L, 8L, 5L))).isEqualTo(",5,8,");
    }

    private UserSessionDto sessionUser(Long userId, Long headquartersId, Long employeeId) {
        UserSessionDto dto = new UserSessionDto();
        dto.setUserId(userId);
        dto.setHeadquartersId(headquartersId);
        dto.setEmployeeId(employeeId);
        dto.setUserType(1);
        return dto;
    }
}
