package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.membre.MembreDTO;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.service.MembreService;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import jakarta.ws.rs.core.Response;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;

/**
 * Tests pour MembreResource
 *
 * @author Lions Dev Team
 * @since 2025-01-10
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("Tests MembreResource")
class MembreResourceTest {

    @InjectMocks
    MembreResource membreResource;

    @Mock
    MembreService membreService;

    @Test
    @DisplayName("Test de l'existence de la classe MembreResource")
    void testMembreResourceExists() {
        // Given & When & Then
        assertThat(MembreResource.class).isNotNull();
        assertThat(membreResource).isNotNull();
    }

    @Test
    @DisplayName("Test de l'annotation Path")
    void testPathAnnotation() {
        // Given & When & Then
        assertThat(MembreResource.class.getAnnotation(jakarta.ws.rs.Path.class))
                .isNotNull();
        assertThat(MembreResource.class.getAnnotation(jakarta.ws.rs.Path.class).value())
                .isEqualTo("/api/membres");
    }

    @Test
    @DisplayName("Test de l'annotation ApplicationScoped")
    void testApplicationScopedAnnotation() {
        // Given & When & Then
        assertThat(MembreResource.class.getAnnotation(jakarta.enterprise.context.ApplicationScoped.class))
                .isNotNull();
    }

    @Test
    @DisplayName("Test de l'annotation Produces")
    void testProducesAnnotation() {
        // Given & When & Then
        assertThat(MembreResource.class.getAnnotation(jakarta.ws.rs.Produces.class))
                .isNotNull();
        assertThat(MembreResource.class.getAnnotation(jakarta.ws.rs.Produces.class).value())
                .contains("application/json");
    }

    @Test
    @DisplayName("Test de l'annotation Consumes")
    void testConsumesAnnotation() {
        // Given & When & Then
        assertThat(MembreResource.class.getAnnotation(jakarta.ws.rs.Consumes.class))
                .isNotNull();
        assertThat(MembreResource.class.getAnnotation(jakarta.ws.rs.Consumes.class).value())
                .contains("application/json");
    }

    @Test
    @DisplayName("Test des méthodes du resource")
    void testResourceMethods() throws NoSuchMethodException {
        // Given & When & Then
        assertThat(MembreResource.class.getMethod("listerMembres")).isNotNull();
        assertThat(MembreResource.class.getMethod("obtenirMembre", Long.class)).isNotNull();
        assertThat(MembreResource.class.getMethod("creerMembre", Membre.class)).isNotNull();
        assertThat(MembreResource.class.getMethod("mettreAJourMembre", Long.class, Membre.class)).isNotNull();
        assertThat(MembreResource.class.getMethod("desactiverMembre", Long.class)).isNotNull();
        assertThat(MembreResource.class.getMethod("rechercherMembres", String.class)).isNotNull();
        assertThat(MembreResource.class.getMethod("obtenirStatistiques")).isNotNull();
    }

    @Test
    @DisplayName("Test de la création d'instance")
    void testInstanceCreation() {
        // Given & When
        MembreResource resource = new MembreResource();

        // Then
        assertThat(resource).isNotNull();
    }

    @Test
    @DisplayName("Test listerMembres")
    void testListerMembres() {
        // Given
        List<Membre> membres = Arrays.asList(
                createTestMembre("Jean", "Dupont"),
                createTestMembre("Marie", "Martin")
        );
        List<MembreDTO> membresDTO = Arrays.asList(
                createTestMembreDTO("Jean", "Dupont"),
                createTestMembreDTO("Marie", "Martin")
        );

        when(membreService.listerMembresActifs(any(Page.class), any(Sort.class))).thenReturn(membres);
        when(membreService.convertToDTOList(membres)).thenReturn(membresDTO);

        // When
        Response response = membreResource.listerMembres(0, 20, "nom", "asc");

        // Then
        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getEntity()).isEqualTo(membresDTO);
    }

    @Test
    @DisplayName("Test obtenirMembre")
    void testObtenirMembre() {
        // Given
        Long id = 1L;
        Membre membre = createTestMembre("Jean", "Dupont");
        when(membreService.trouverParId(id)).thenReturn(Optional.of(membre));

        // When
        Response response = membreResource.obtenirMembre(id);

        // Then
        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getEntity()).isEqualTo(membre);
    }

    @Test
    @DisplayName("Test obtenirMembre - membre non trouvé")
    void testObtenirMembreNonTrouve() {
        // Given
        Long id = 999L;
        when(membreService.trouverParId(id)).thenReturn(Optional.empty());

        // When
        Response response = membreResource.obtenirMembre(id);

        // Then
        assertThat(response.getStatus()).isEqualTo(404);
    }

    @Test
    @DisplayName("Test creerMembre")
    void testCreerMembre() {
        // Given
        MembreDTO membreDTO = createTestMembreDTO("Jean", "Dupont");
        Membre membre = createTestMembre("Jean", "Dupont");
        Membre membreCreated = createTestMembre("Jean", "Dupont");
        membreCreated.id = 1L;
        MembreDTO membreCreatedDTO = createTestMembreDTO("Jean", "Dupont");

        when(membreService.convertFromDTO(any(MembreDTO.class))).thenReturn(membre);
        when(membreService.creerMembre(any(Membre.class))).thenReturn(membreCreated);
        when(membreService.convertToDTO(any(Membre.class))).thenReturn(membreCreatedDTO);

        // When
        Response response = membreResource.creerMembre(membreDTO);

        // Then
        assertThat(response.getStatus()).isEqualTo(201);
        assertThat(response.getEntity()).isEqualTo(membreCreatedDTO);
    }

    @Test
    @DisplayName("Test mettreAJourMembre")
    void testMettreAJourMembre() {
        // Given
        Long id = 1L;
        MembreDTO membreDTO = createTestMembreDTO("Jean", "Dupont");
        Membre membre = createTestMembre("Jean", "Dupont");
        Membre membreUpdated = createTestMembre("Jean", "Martin");
        membreUpdated.id = id;
        MembreDTO membreUpdatedDTO = createTestMembreDTO("Jean", "Martin");

        when(membreService.convertFromDTO(any(MembreDTO.class))).thenReturn(membre);
        when(membreService.mettreAJourMembre(anyLong(), any(Membre.class))).thenReturn(membreUpdated);
        when(membreService.convertToDTO(any(Membre.class))).thenReturn(membreUpdatedDTO);

        // When
        Response response = membreResource.mettreAJourMembre(id, membreDTO);

        // Then
        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getEntity()).isEqualTo(membreUpdatedDTO);
    }

    @Test
    @DisplayName("Test desactiverMembre")
    void testDesactiverMembre() {
        // Given
        Long id = 1L;

        // When
        Response response = membreResource.desactiverMembre(id);

        // Then
        assertThat(response.getStatus()).isEqualTo(204);
    }

    @Test
    @DisplayName("Test rechercherMembres")
    void testRechercherMembres() {
        // Given
        String recherche = "Jean";
        List<Membre> membres = Arrays.asList(createTestMembre("Jean", "Dupont"));
        List<MembreDTO> membresDTO = Arrays.asList(createTestMembreDTO("Jean", "Dupont"));
        when(membreService.rechercherMembres(anyString(), any(Page.class), any(Sort.class))).thenReturn(membres);
        when(membreService.convertToDTOList(membres)).thenReturn(membresDTO);

        // When
        Response response = membreResource.rechercherMembres(recherche, 0, 20, "nom", "asc");

        // Then
        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getEntity()).isEqualTo(membresDTO);
    }

    @Test
    @DisplayName("Test obtenirStatistiques")
    void testObtenirStatistiques() {
        // Given
        long count = 42L;
        when(membreService.compterMembresActifs()).thenReturn(count);

        // When
        Response response = membreResource.obtenirStatistiques();

        // Then
        assertThat(response.getStatus()).isEqualTo(200);
        assertThat(response.getEntity()).isInstanceOf(java.util.Map.class);
    }

    private Membre createTestMembre(String prenom, String nom) {
        Membre membre = new Membre();
        membre.setPrenom(prenom);
        membre.setNom(nom);
        membre.setEmail(prenom.toLowerCase() + "." + nom.toLowerCase() + "@test.com");
        membre.setTelephone("221701234567");
        membre.setDateNaissance(LocalDate.of(1990, 1, 1));
        membre.setDateAdhesion(LocalDate.now());
        membre.setActif(true);
        membre.setNumeroMembre("UF-2025-TEST01");
        return membre;
    }

    private MembreDTO createTestMembreDTO(String prenom, String nom) {
        MembreDTO dto = new MembreDTO();
        dto.setPrenom(prenom);
        dto.setNom(nom);
        dto.setEmail(prenom.toLowerCase() + "." + nom.toLowerCase() + "@test.com");
        dto.setTelephone("221701234567");
        dto.setDateNaissance(LocalDate.of(1990, 1, 1));
        dto.setDateAdhesion(LocalDate.now());
        dto.setStatut("ACTIF");
        dto.setNumeroMembre("UF-2025-TEST01");
        dto.setAssociationId(1L);
        return dto;
    }
}
