package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import io.restassured.http.ContentType;

import dev.lions.unionflow.server.api.dto.solidarite.response.DemandeAideResponse;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.service.DemandeAideService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests avec mocks pour couvrir les branches manquantes de DemandeAideResource.
 *
 * <p>Branches ciblées:
 * <ul>
 *   <li>mesDemandes: from < to = true (membre avec demandes → pagination réelle)</li>
 *   <li>listerToutes: from < to = true (résultats non-vides)</li>
 *   <li>rechercher: from < to = true (résultats non-vides)</li>
 *   <li>obtenirParId: response != null (found)</li>
 * </ul>
 *
 * @author UnionFlow Team
 */
@QuarkusTest
class DemandeAideMockResourceTest {

    @InjectMock
    DemandeAideService demandeAideService;

    @InjectMock
    MembreRepository membreRepository;

    private DemandeAideResponse buildResponse() {
        DemandeAideResponse r = new DemandeAideResponse();
        r.setId(UUID.randomUUID());
        r.setTypeAide(TypeAide.AIDE_FINANCIERE_URGENTE);
        r.setStatut(StatutAide.EN_ATTENTE);
        return r;
    }

    // -------------------------------------------------------------------------
    // mesDemandes — from < to = true
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/demandes-aide/mes avec membre et demandes → from < to true (liste retournée)")
    void mesDemandes_membreAvecDemandes_fromLtTo_returnsResults() {
        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        membre.setActif(true);

        DemandeAideResponse d1 = buildResponse();
        DemandeAideResponse d2 = buildResponse();

        when(membreRepository.findByEmail(anyString())).thenReturn(Optional.of(membre));
        when(demandeAideService.rechercherAvecFiltres(any())).thenReturn(List.of(d1, d2));

        given()
                .queryParam("page", 0)
                .queryParam("size", 10)
                .when()
                .get("/api/demandes-aide/mes")
                .then()
                .statusCode(200)
                .body("$", hasSize(2));
    }

    // -------------------------------------------------------------------------
    // listerToutes — from < to = true
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/demandes-aide avec résultats → from < to true (liste retournée)")
    void listerToutes_avecResultats_fromLtTo_returnsResults() {
        DemandeAideResponse d = buildResponse();

        when(demandeAideService.rechercherAvecFiltres(any())).thenReturn(List.of(d));

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get("/api/demandes-aide")
                .then()
                .statusCode(200)
                .body("$", hasSize(1));
    }

    // -------------------------------------------------------------------------
    // rechercher — from < to = true
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/demandes-aide/search avec résultats → from < to true (liste retournée)")
    void rechercher_avecResultats_fromLtTo_returnsResults() {
        DemandeAideResponse d = buildResponse();

        when(demandeAideService.rechercherAvecFiltres(any())).thenReturn(List.of(d));

        given()
                .queryParam("statut", "EN_ATTENTE")
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200)
                .body("$", hasSize(1));
    }

    // -------------------------------------------------------------------------
    // mesDemandes — from >= to (page beyond data → empty list, L61 false branch)
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/demandes-aide/mes page hors limites → from >= to false → liste vide (L61 false)")
    void mesDemandes_pageBeyondData_fromGteTo_returnsEmpty() {
        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        member(membre);

        // 1 demande, but requesting page=10 (size=50, from=500 >= to=1)
        DemandeAideResponse d = buildResponse();
        when(demandeAideService.rechercherAvecFiltres(any())).thenReturn(List.of(d));

        given()
                .queryParam("page", 10)  // page 10 with 1 result → from=10*50=500 >= all.size()=1
                .queryParam("size", 50)
                .when()
                .get("/api/demandes-aide/mes")
                .then()
                .statusCode(200)
                .body("$", hasSize(0));
    }

    private void member(Membre membre) {
        when(membreRepository.findByEmail(anyString())).thenReturn(Optional.of(membre));
    }

    // -------------------------------------------------------------------------
    // Error cases
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("GET /api/demandes-aide/mes sans authentification retourne 401")
    void mesDemandes_sansAuthentification_returns401() {
        given()
                .when()
                .get("/api/demandes-aide/mes")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /api/demandes-aide sans authentification retourne 401")
    void listerToutes_sansAuthentification_returns401() {
        given()
                .when()
                .get("/api/demandes-aide")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/demandes-aide/{id} avec ID inexistant retourne 404")
    void obtenirParId_notFound_returns404() {
        UUID id = UUID.randomUUID();
        when(demandeAideService.obtenirParId(any(UUID.class))).thenReturn(null);

        given()
                .pathParam("id", id)
                .when()
                .get("/api/demandes-aide/{id}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT /api/demandes-aide/{id}/approuver avec rôle MEMBRE retourne 403")
    void approuver_avecRoleMembre_returns403() {
        UUID id = UUID.randomUUID();

        given()
                .queryParam("motif", "Test")
                .pathParam("id", id)
                .when()
                .put("/api/demandes-aide/{id}/approuver")
                .then()
                .statusCode(403);
    }

    // -------------------------------------------------------------------------
    // obtenirParId — response != null (found)
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/demandes-aide/{id} avec réponse non-null → retourne 200 (branche response != null)")
    void obtenirParId_found_returns200() {
        UUID id = UUID.randomUUID();
        DemandeAideResponse d = buildResponse();
        d.setId(id);

        when(demandeAideService.obtenirParId(org.mockito.ArgumentMatchers.any(UUID.class))).thenReturn(d);

        given()
                .pathParam("id", id)
                .when()
                .get("/api/demandes-aide/{id}")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // approuver — mock retourne réponse → méthode couverte
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/demandes-aide/{id}/approuver → mock retourne réponse → 200 (méthode couverte)")
    void approuver_mockRetourneReponse_returns200() {
        UUID id = UUID.randomUUID();
        when(demandeAideService.changerStatut(any(), any(), any())).thenReturn(buildResponse());

        given()
                .queryParam("motif", "Test approbation")
                .pathParam("id", id)
                .when()
                .put("/api/demandes-aide/{id}/approuver")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // rejeter — mock retourne réponse → méthode couverte
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/demandes-aide/{id}/rejeter → mock retourne réponse → 200 (méthode couverte)")
    void rejeter_mockRetourneReponse_returns200() {
        UUID id = UUID.randomUUID();
        when(demandeAideService.changerStatut(any(), any(), any())).thenReturn(buildResponse());

        given()
                .queryParam("motif", "Test rejet")
                .pathParam("id", id)
                .when()
                .put("/api/demandes-aide/{id}/rejeter")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // mettreAJour — mock retourne réponse → méthode couverte
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/demandes-aide/{id} → mock retourne réponse → 200 (méthode couverte)")
    void mettreAJour_mockRetourneReponse_returns200() {
        UUID id = UUID.randomUUID();
        when(demandeAideService.mettreAJour(any(), any())).thenReturn(buildResponse());

        given()
                .contentType(ContentType.JSON)
                .body("{\"titre\": \"Titre mis à jour\"}")
                .pathParam("id", id)
                .when()
                .put("/api/demandes-aide/{id}")
                .then()
                .statusCode(200);
    }
}
