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.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.FeedbackEvenement;
import dev.lions.unionflow.server.entity.InscriptionEvenement;
import dev.lions.unionflow.server.service.EvenementService;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
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.Map;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests mock pour EvenementResource — couvre desinscrireEvenement et getMesInscriptions.
 */
@QuarkusTest
@DisplayName("EvenementResource (mock)")
class EvenementResourceMockTest {

    private static final String BASE_PATH = "/api/evenements";

    @InjectMock
    EvenementService evenementService;

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("DELETE /{id}/inscriptions — se désinscrire retourne 204")
    void desinscrireEvenement_success_returns204() {
        doNothing().when(evenementService).desinscrireEvenement(any(UUID.class));

        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .delete(BASE_PATH + "/{id}/inscriptions")
                .then()
                .statusCode(204);
    }

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("GET /mes-inscriptions — retourne 200 avec liste vide")
    void getMesInscriptions_success_returns200() {
        when(evenementService.getMesInscriptions()).thenReturn(Collections.<InscriptionEvenement>emptyList());

        given()
                .when()
                .get(BASE_PATH + "/mes-inscriptions")
                .then()
                .statusCode(200);
    }

    // =========================================================================
    // POST /{id}/feedback — soumetteFeedback (lignes 374-381)
    // =========================================================================

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST /{id}/feedback avec note valide — retourne 201 (couvre lignes 374-376)")
    void soumetteFeedback_validNote_returns201() {
        FeedbackEvenement feedback = new FeedbackEvenement();
        feedback.setNote(4);
        feedback.setDateFeedback(LocalDateTime.now());

        when(evenementService.soumetteFeedback(any(UUID.class), anyInt(), anyString()))
                .thenReturn(feedback);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .body("{\"note\":4,\"commentaire\":\"Super événement\"}")
                .when()
                .post(BASE_PATH + "/{id}/feedback")
                .then()
                .statusCode(201);
    }

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST /{id}/feedback lève IllegalStateException — retourne 400 (couvre lignes 378-381)")
    void soumetteFeedback_illegalState_returns400() {
        when(evenementService.soumetteFeedback(any(UUID.class), anyInt(), any()))
                .thenThrow(new IllegalStateException("Feedback déjà soumis"));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .body("{\"note\":3}")
                .when()
                .post(BASE_PATH + "/{id}/feedback")
                .then()
                .statusCode(400);
    }

    // =========================================================================
    // POST /{id}/inscriptions — inscrireEvenement IllegalStateException (ligne 309-312)
    // =========================================================================

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST /{id}/inscriptions lève IllegalStateException — retourne 400 (L308-312)")
    void inscrireEvenement_illegalState_returns400() {
        when(evenementService.inscrireEvenement(any(UUID.class)))
                .thenThrow(new IllegalStateException("Événement complet ou déjà inscrit"));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .when()
                .post(BASE_PATH + "/{id}/inscriptions")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST /{id}/inscriptions réussi — retourne 201 (L306-307)")
    void inscrireEvenement_success_returns201() {
        InscriptionEvenement inscription = new InscriptionEvenement();
        when(evenementService.inscrireEvenement(any(UUID.class))).thenReturn(inscription);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .when()
                .post(BASE_PATH + "/{id}/inscriptions")
                .then()
                .statusCode(201);
    }

    // =========================================================================
    // GET / listerEvenements — branch try/catch conversion error (lignes 107-109)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET / listerEvenements — conversion DTO lève exception → continue (couvre L107-109)")
    void listerEvenements_dtoConversionError_continue() {
        // Créer un Evenement qui causera une erreur lors de EvenementMobileDTO.fromEntity()
        // en ayant des données invalides (titre null par exemple)
        Evenement evenementInvalide = new Evenement();
        // id non défini → UUID null → fromEntity va probablement lever une exception
        // Cela couvre le catch block (ligne 107-109) qui log l'erreur et continue

        when(evenementService.listerEvenementsActifs(any(Page.class), any(Sort.class)))
                .thenReturn(List.of(evenementInvalide));
        when(evenementService.countEvenementsActifs()).thenReturn(1L);

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                // Peut retourner 200 (si conversion partielle réussit) ou 500 (si exception non catchée)
                // Le catch bloc interne absorbe et continue → résultat est 200 avec liste potentiellement vide
                .statusCode(200);
    }

    // =========================================================================
    // GET / listerEvenements — direction desc (branch L93-95)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET / listerEvenements avec direction=desc (branche descending L94)")
    void listerEvenements_descSort_returns200() {
        when(evenementService.listerEvenementsActifs(any(Page.class), any(Sort.class)))
                .thenReturn(List.of());
        when(evenementService.countEvenementsActifs()).thenReturn(0L);

        given()
                .queryParam("page", 0)
                .queryParam("size", 10)
                .queryParam("sort", "titre")
                .queryParam("direction", "desc")
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200);
    }

    // =========================================================================
    // GET / listerEvenements — happy path complet (couvre EvenementResource:91 — 8I, 0B)
    // =========================================================================

    /**
     * Couvre les instructions manquantes à la ligne 91 (début de listerEvenements).
     *
     * <p>Fournit une liste d'événements valides avec un ID non null pour que
     * {@code EvenementMobileDTO.fromEntity()} réussisse et que tous les logs soient exécutés.
     */
    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET / listerEvenements avec événements valides — couvre les instructions ligne 91 (8I, 0B)")
    void listerEvenements_avecEvenementsValides_couvreL91() {
        Evenement evt = new Evenement();
        evt.setId(UUID.randomUUID());
        evt.setTitre("Événement Mock");
        evt.setStatut("PLANIFIE");
        evt.setTypeEvenement("REUNION");
        evt.setDateDebut(LocalDateTime.now().plusDays(5));
        evt.setDateCreation(LocalDateTime.now());
        evt.setActif(true);

        when(evenementService.listerEvenementsActifs(any(Page.class), any(Sort.class)))
                .thenReturn(List.of(evt));
        when(evenementService.countEvenementsActifs()).thenReturn(1L);

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .queryParam("sort", "dateDebut")
                .queryParam("direction", "asc")
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .body("total", equalTo(1))
                .body("page", equalTo(0));
    }

    // =========================================================================
    // POST /{id}/feedback — note == null (branch L367: note == null → 400)
    // =========================================================================

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST /{id}/feedback sans note (null) — retourne 400 (L367: note == null)")
    void soumetteFeedback_noteMissing_returns400() {
        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .body("{\"commentaire\":\"Pas de note fournie\"}")
                .when()
                .post(BASE_PATH + "/{id}/feedback")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.notNullValue());
    }

    // =========================================================================
    // GET /recherche — rechercherEvenements (missed branch: blank recherche → 400)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /recherche avec q vide (blank) — retourne 400 (branche recherche.trim().isEmpty())")
    void rechercherEvenements_blankRecherche_returns400() {
        // q=" " → trim() → "" → isEmpty() = true → 400
        given()
                .queryParam("q", "   ")
                .when()
                .get(BASE_PATH + "/recherche")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /recherche avec q absent (null) — retourne 400 (branche recherche == null)")
    void rechercherEvenements_nullRecherche_returns400() {
        // No q param → recherche = null → null condition → 400
        given()
                .when()
                .get(BASE_PATH + "/recherche")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.notNullValue());
    }

    // =========================================================================
    // GET /{id}/feedbacks — getFeedbacks (lignes 390-398)
    // =========================================================================

    @Test
    @DisplayName("GET /{id}/feedbacks retourne 200 avec feedbacks et stats (L390-398)")
    void getFeedbacks_success_returns200() {
        FeedbackEvenement feedback = new FeedbackEvenement();
        feedback.setNote(4);
        feedback.setDateFeedback(LocalDateTime.now());

        Map<String, Object> stats = Map.of("noteMoyenne", 4.0, "nombreFeedbacks", 1L);

        when(evenementService.getFeedbacks(any(UUID.class))).thenReturn(List.of(feedback));
        when(evenementService.getStatistiquesFeedback(any(UUID.class))).thenReturn(stats);

        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .get(BASE_PATH + "/{id}/feedbacks")
                .then()
                .statusCode(200)
                .body("feedbacks", org.hamcrest.Matchers.notNullValue());
    }
}
