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

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.tontine.TontineRequest;
import dev.lions.unionflow.server.api.dto.tontine.TontineResponse;
import dev.lions.unionflow.server.api.enums.tontine.StatutTontine;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.tontine.Tontine;
import dev.lions.unionflow.server.mapper.tontine.TontineMapper;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.repository.tontine.TontineRepository;
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 TontineServiceTest {

    @Inject
    TontineService service;

    @InjectMock
    TontineRepository repository;

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    TontineMapper mapper;

    @Test
    @DisplayName("creerTontine initialise le statut à PLANIFIEE")
    void creerTontine_success() {
        UUID orgId = UUID.randomUUID();
        TontineRequest request = new TontineRequest();
        request.setOrganisationId(orgId.toString());

        Organisation org = new Organisation();
        org.setId(orgId);

        Tontine entity = new Tontine();
        TontineResponse responseDto = new TontineResponse();

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(mapper.toEntity(request)).thenReturn(entity);
        when(mapper.toDto(entity)).thenReturn(responseDto);

        service.creerTontine(request);

        assertThat(entity.getStatut()).isEqualTo(StatutTontine.PLANIFIEE);
        assertThat(entity.getOrganisation()).isEqualTo(org);
        verify(repository).persist(any(Tontine.class));
    }

    @Test
    @DisplayName("creerTontine lance NotFoundException si l'organisation n'existe pas")
    void creerTontine_orgNotFound_throwsNotFound() {
        UUID orgId = UUID.randomUUID();
        TontineRequest request = new TontineRequest();
        request.setOrganisationId(orgId.toString());

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

        assertThatThrownBy(() -> service.creerTontine(request))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("getTontineById retourne le DTO quand la tontine existe")
    void getTontineById_found_returnsDto() {
        UUID id = UUID.randomUUID();
        Tontine entity = new Tontine();
        TontineResponse dto = new TontineResponse();

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

        TontineResponse result = service.getTontineById(id);

        assertThat(result).isEqualTo(dto);
    }

    @Test
    @DisplayName("getTontineById lance NotFoundException si la tontine n'existe pas")
    void getTontineById_notFound_throws() {
        UUID id = UUID.randomUUID();
        when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

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

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("getTontinesByOrganisation retourne la liste des tontines")
    void getTontinesByOrganisation_returnsList() {
        UUID orgId = UUID.randomUUID();
        Tontine entity = new Tontine();
        TontineResponse dto = new TontineResponse();

        PanacheQuery<Tontine> 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<TontineResponse> result = service.getTontinesByOrganisation(orgId);

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

    @Test
    @DisplayName("changerStatut met à jour le statut de la tontine")
    void changerStatut_found_success() {
        UUID id = UUID.randomUUID();
        Tontine entity = new Tontine();
        TontineResponse dto = new TontineResponse();

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

        TontineResponse result = service.changerStatut(id, StatutTontine.EN_COURS);

        assertThat(entity.getStatut()).isEqualTo(StatutTontine.EN_COURS);
        assertThat(result).isEqualTo(dto);
    }

    @Test
    @DisplayName("changerStatut lance NotFoundException si la tontine n'existe pas")
    void changerStatut_notFound_throws() {
        UUID id = UUID.randomUUID();
        when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.changerStatut(id, StatutTontine.EN_COURS))
                .isInstanceOf(NotFoundException.class);
    }
}
