package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.solidarite.request.UpdatePropositionAideRequest;
import dev.lions.unionflow.server.api.dto.solidarite.response.PropositionAideResponse;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.service.PropositionAideService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests mock pour PropositionAideResource — couvre mettreAJour.
 */
@QuarkusTest
@DisplayName("PropositionAideResource (mock)")
class PropositionAideMockResourceTest {

    @InjectMock
    PropositionAideService propositionAideService;

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/propositions-aide/{id} — mettreAJour retourne la réponse du service")
    void mettreAJour_success_returnsResponse() {
        PropositionAideResponse response = new PropositionAideResponse();
        when(propositionAideService.mettreAJour(anyString(), any(UpdatePropositionAideRequest.class)))
                .thenReturn(response);

        String body = """
                {
                  "typeAide": "AIDE_FRAIS_MEDICAUX",
                  "titre": "Aide médicale mise à jour",
                  "description": "Description mise à jour"
                }
                """;

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", "prop-test-id-001")
                .body(body)
                .when()
                .put("/api/propositions-aide/{id}")
                .then()
                .statusCode(200);
    }

    // =========================================================================
    // listerToutes:35 — branche from >= to (liste vide après pagination) (5I, 1B)
    // =========================================================================

    /**
     * Couvre la branche {@code from >= to} (ternaire retournant {@code List.of()}) à la ligne 38
     * de {@code listerToutes}.
     *
     * <p>Quand la liste est vide et page=5 (from=100 > all.size()=0 → from == to == 0 mais
     * page*size dépasse la taille), la branche {@code from < to ? ... : List.of()} prend le
     * chemin {@code List.of()}.
     */
    @Test
    @DisplayName("GET /api/propositions-aide — liste vide retourne [] (branche from >= to, L38 ternaire false)")
    void listerToutes_listeVide_retourneListeVide() {
        when(propositionAideService.rechercherAvecFiltres(anyMap()))
                .thenReturn(Collections.emptyList());

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

    /**
     * Couvre la branche {@code from < to} (ternaire retournant la sous-liste) à la ligne 38.
     *
     * <p>Quand la liste contient des éléments et page=0, {@code from=0, to=min(20, size)}
     * → branche {@code from < to} true → retourne {@code all.subList(from, to)}.
     * Couvre aussi les instructions manquantes à la ligne 35 (début de listerToutes).
     */
    @Test
    @DisplayName("GET /api/propositions-aide — liste non vide retourne sous-liste (branche from < to, L38 ternaire true)")
    void listerToutes_listeNonVide_retourneSousListe() {
        PropositionAideResponse prop1 = new PropositionAideResponse();
        prop1.setId(UUID.randomUUID());
        prop1.setTitre("Aide test mock 1");
        prop1.setTypeAide(TypeAide.AIDE_ALIMENTAIRE);
        prop1.setDateCreation(LocalDateTime.now());

        PropositionAideResponse prop2 = new PropositionAideResponse();
        prop2.setId(UUID.randomUUID());
        prop2.setTitre("Aide test mock 2");
        prop2.setTypeAide(TypeAide.AIDE_FRAIS_MEDICAUX);
        prop2.setDateCreation(LocalDateTime.now());

        when(propositionAideService.rechercherAvecFiltres(anyMap()))
                .thenReturn(List.of(prop1, prop2));

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

    // =========================================================================
    // obtenirParId — branche found (retourne la réponse) et not found (404)
    // =========================================================================

    /**
     * Couvre la branche {@code response != null} de {@code obtenirParId} dans la resource.
     *
     * <p>Quand le service retourne une réponse non-null, la resource la retourne directement
     * (HTTP 200) sans lever de NotFoundException.
     */
    @Test
    @DisplayName("GET /api/propositions-aide/{id} — service retourne réponse → 200 (branche found)")
    void obtenirParId_found_returns200() {
        PropositionAideResponse prop = new PropositionAideResponse();
        prop.setId(UUID.randomUUID());
        prop.setTitre("Aide trouvée");
        prop.setTypeAide(TypeAide.TRANSPORT);
        prop.setDateCreation(LocalDateTime.now());

        when(propositionAideService.obtenirParId("id-existant-mock")).thenReturn(prop);

        given()
                .pathParam("id", "id-existant-mock")
                .when()
                .get("/api/propositions-aide/{id}")
                .then()
                .statusCode(200)
                .body("titre", equalTo("Aide trouvée"));
    }

    // =========================================================================
    // Error cases
    // =========================================================================

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

    @Test
    @DisplayName("GET /api/propositions-aide/{id} sans authentification retourne 401")
    void obtenirParId_sansAuthentification_returns401() {
        given()
                .pathParam("id", "id-quelconque")
                .when()
                .get("/api/propositions-aide/{id}")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/propositions-aide/{id} — service retourne null → 404 (branche not found)")
    void obtenirParId_notFound_returns404() {
        when(propositionAideService.obtenirParId("id-inexistant")).thenReturn(null);

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

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("PUT /api/propositions-aide/{id} avec rôle MEMBRE retourne 403")
    void mettreAJour_avecRoleMembre_returns403() {
        String body = """
                {
                  "typeAide": "AIDE_FRAIS_MEDICAUX",
                  "titre": "Aide non autorisée"
                }
                """;

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", "prop-id-001")
                .body(body)
                .when()
                .put("/api/propositions-aide/{id}")
                .then()
                .statusCode(403);
    }
}
