package dev.lions.unionflow.server.service.culte;

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.anyString;
import static org.mockito.Mockito.*;

import dev.lions.unionflow.server.api.dto.culte.DonReligieuxDTO;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.culte.DonReligieux;
import dev.lions.unionflow.server.mapper.culte.DonReligieuxMapper;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.repository.culte.DonReligieuxRepository;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class DonReligieuxServiceTest {

    @Inject
    DonReligieuxService service;

    @InjectMock
    DonReligieuxRepository repository;

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    DonReligieuxMapper mapper;

    @InjectMock
    MembreRepository membreRepository;

    @Test
    @DisplayName("enregistrerDon persiste le don et définit la date d'encaissement")
    void enregistrerDon_success() {
        UUID instId = UUID.randomUUID();
        DonReligieuxDTO dto = new DonReligieuxDTO();
        dto.setInstitutionId(instId.toString());

        Organisation inst = new Organisation();
        inst.setId(instId);

        DonReligieux entity = new DonReligieux();

        when(organisationRepository.findByIdOptional(instId)).thenReturn(Optional.of(inst));
        when(mapper.toEntity(dto)).thenReturn(entity);
        when(mapper.toDto(entity)).thenReturn(dto);

        service.enregistrerDon(dto);

        assertThat(entity.getDateEncaissement()).isNotNull();
        assertThat(entity.getInstitution()).isEqualTo(inst);
        verify(repository).persist(any(DonReligieux.class));
    }

    @Test
    @DisplayName("enregistrerDon lance NotFoundException si l'organisation n'existe pas")
    void enregistrerDon_orgNotFound_throwsNotFound() {
        UUID instId = UUID.randomUUID();
        DonReligieuxDTO dto = new DonReligieuxDTO();
        dto.setInstitutionId(instId.toString());

        when(organisationRepository.findByIdOptional(instId)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.enregistrerDon(dto))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("enregistrerDon lance NotFoundException si le fidèle n'existe pas")
    void enregistrerDon_fideleNotFound_throwsNotFound() {
        UUID instId = UUID.randomUUID();
        UUID fideleId = UUID.randomUUID();
        DonReligieuxDTO dto = new DonReligieuxDTO();
        dto.setInstitutionId(instId.toString());
        dto.setFideleId(fideleId.toString());

        Organisation inst = new Organisation();
        inst.setId(instId);
        DonReligieux entity = new DonReligieux();

        when(organisationRepository.findByIdOptional(instId)).thenReturn(Optional.of(inst));
        when(mapper.toEntity(dto)).thenReturn(entity);
        when(membreRepository.findByIdOptional(fideleId)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.enregistrerDon(dto))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("getDonById retourne le DTO quand le don existe")
    void getDonById_found_returnsDto() {
        UUID id = UUID.randomUUID();
        DonReligieux entity = new DonReligieux();
        DonReligieuxDTO dto = new DonReligieuxDTO();

        when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
        when(mapper.toDto(entity)).thenReturn(dto);

        DonReligieuxDTO result = service.getDonById(id);

        assertThat(result).isEqualTo(dto);
    }

    @Test
    @DisplayName("getDonById lance NotFoundException si le don n'existe pas")
    void getDonById_notFound_throws() {
        UUID id = UUID.randomUUID();
        when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.getDonById(id))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("getDonsByOrganisation retourne la liste des dons de l'organisation")
    void getDonsByOrganisation_returnsList() {
        UUID orgId = UUID.randomUUID();
        DonReligieux entity = new DonReligieux();
        DonReligieuxDTO dto = new DonReligieuxDTO();

        PanacheQuery<DonReligieux> mockQuery = mock(PanacheQuery.class);
        when(repository.find(anyString(), (Object[]) any())).thenReturn(mockQuery);
        when(mockQuery.stream()).thenReturn(List.of(entity).stream());
        when(mapper.toDto(entity)).thenReturn(dto);

        List<DonReligieuxDTO> result = service.getDonsByOrganisation(orgId);

        assertThat(result).hasSize(1).containsExactly(dto);
    }

    @Test
    @DisplayName("enregistrerDon avec fidèle trouvé définit le fidèle sur le don")
    void enregistrerDon_avecFideleTrouve_setFidele() {
        UUID instId = UUID.randomUUID();
        UUID fideleId = UUID.randomUUID();
        DonReligieuxDTO dto = new DonReligieuxDTO();
        dto.setInstitutionId(instId.toString());
        dto.setFideleId(fideleId.toString());

        Organisation inst = new Organisation();
        inst.setId(instId);

        Membre membre = new Membre();
        membre.setId(fideleId);

        DonReligieux entity = new DonReligieux();

        when(organisationRepository.findByIdOptional(instId)).thenReturn(Optional.of(inst));
        when(mapper.toEntity(dto)).thenReturn(entity);
        when(membreRepository.findByIdOptional(fideleId)).thenReturn(Optional.of(membre));
        when(mapper.toDto(entity)).thenReturn(dto);

        service.enregistrerDon(dto);

        assertThat(entity.getFidele()).isEqualTo(membre);
        verify(repository).persist(any(DonReligieux.class));
    }
}
