package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.Evenement.StatutEvenement;
import dev.lions.unionflow.server.entity.Evenement.TypeEvenement;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.*;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;

/**
 * Tests d'intégration pour EvenementResource
 * 
 * Tests complets de l'API REST des événements avec authentification
 * et validation des permissions. Optimisé pour l'intégration mobile.
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DisplayName("Tests d'intégration - API Événements")
class EvenementResourceTest {

    private static Long evenementTestId;
    private static Long organisationTestId;
    private static Long membreTestId;

    @BeforeAll
    @Transactional
    static void setupTestData() {
        // Créer une organisation de test
        Organisation organisation = Organisation.builder()
            .nom("Union Test API")
            .typeOrganisation("ASSOCIATION")
            .statut("ACTIVE")
            .email("test-api@union.com")
            .telephone("0123456789")
            .adresse("123 Rue de Test")
            .codePostal("75001")
            .ville("Paris")
            .pays("France")
            .actif(true)
            .creePar("test@unionflow.dev")
            .dateCreation(LocalDateTime.now())
            .build();
        organisation.persist();
        organisationTestId = organisation.id;

        // Créer un membre de test
        Membre membre = Membre.builder()
            .numeroMembre("UF2025-API01")
            .prenom("Marie")
            .nom("Martin")
            .email("marie.martin@test.com")
            .telephone("0987654321")
            .dateNaissance(LocalDate.of(1990, 5, 15))
            .dateAdhesion(LocalDate.now())
            .actif(true)
            .organisation(organisation)
            .build();
        membre.persist();
        membreTestId = membre.id;

        // Créer un événement de test
        Evenement evenement = Evenement.builder()
            .titre("Conférence API Test")
            .description("Conférence de test pour l'API")
            .dateDebut(LocalDateTime.now().plusDays(15))
            .dateFin(LocalDateTime.now().plusDays(15).plusHours(2))
            .lieu("Centre de conférence Test")
            .typeEvenement(TypeEvenement.CONFERENCE)
            .statut(StatutEvenement.PLANIFIE)
            .capaciteMax(50)
            .prix(BigDecimal.valueOf(15.00))
            .inscriptionRequise(true)
            .visiblePublic(true)
            .actif(true)
            .organisation(organisation)
            .organisateur(membre)
            .creePar("test@unionflow.dev")
            .dateCreation(LocalDateTime.now())
            .build();
        evenement.persist();
        evenementTestId = evenement.id;
    }

    @Test
    @Order(1)
    @DisplayName("GET /api/evenements - Lister événements (authentifié)")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testListerEvenements_Authentifie() {
        given()
            .when()
            .get("/api/evenements")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("size()", greaterThanOrEqualTo(1))
            .body("[0].titre", notNullValue())
            .body("[0].dateDebut", notNullValue())
            .body("[0].statut", notNullValue());
    }

    @Test
    @Order(2)
    @DisplayName("GET /api/evenements - Non authentifié")
    void testListerEvenements_NonAuthentifie() {
        given()
            .when()
            .get("/api/evenements")
            .then()
            .statusCode(401);
    }

    @Test
    @Order(3)
    @DisplayName("GET /api/evenements/{id} - Récupérer événement")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testObtenirEvenement() {
        given()
            .pathParam("id", evenementTestId)
            .when()
            .get("/api/evenements/{id}")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("id", equalTo(evenementTestId.intValue()))
            .body("titre", equalTo("Conférence API Test"))
            .body("description", equalTo("Conférence de test pour l'API"))
            .body("typeEvenement", equalTo("CONFERENCE"))
            .body("statut", equalTo("PLANIFIE"))
            .body("capaciteMax", equalTo(50))
            .body("prix", equalTo(15.0f))
            .body("inscriptionRequise", equalTo(true))
            .body("visiblePublic", equalTo(true))
            .body("actif", equalTo(true));
    }

    @Test
    @Order(4)
    @DisplayName("GET /api/evenements/{id} - Événement non trouvé")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testObtenirEvenement_NonTrouve() {
        given()
            .pathParam("id", 99999)
            .when()
            .get("/api/evenements/{id}")
            .then()
            .statusCode(404)
            .body("error", equalTo("Événement non trouvé"));
    }

    @Test
    @Order(5)
    @DisplayName("POST /api/evenements - Créer événement (organisateur)")
    @TestSecurity(user = "marie.martin@test.com", roles = {"ORGANISATEUR_EVENEMENT"})
    void testCreerEvenement_Organisateur() {
        String nouvelEvenement = String.format("""
            {
                "titre": "Nouvel Événement Test",
                "description": "Description du nouvel événement",
                "dateDebut": "%s",
                "dateFin": "%s",
                "lieu": "Lieu de test",
                "typeEvenement": "FORMATION",
                "capaciteMax": 30,
                "prix": 20.00,
                "inscriptionRequise": true,
                "visiblePublic": true,
                "organisation": {"id": %d},
                "organisateur": {"id": %d}
            }
            """,
            LocalDateTime.now().plusDays(20).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
            LocalDateTime.now().plusDays(20).plusHours(3).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
            organisationTestId,
            membreTestId
        );

        given()
            .contentType(ContentType.JSON)
            .body(nouvelEvenement)
            .when()
            .post("/api/evenements")
            .then()
            .statusCode(201)
            .contentType(ContentType.JSON)
            .body("titre", equalTo("Nouvel Événement Test"))
            .body("typeEvenement", equalTo("FORMATION"))
            .body("capaciteMax", equalTo(30))
            .body("prix", equalTo(20.0f))
            .body("actif", equalTo(true));
    }

    @Test
    @Order(6)
    @DisplayName("POST /api/evenements - Permissions insuffisantes")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testCreerEvenement_PermissionsInsuffisantes() {
        String nouvelEvenement = """
            {
                "titre": "Événement Non Autorisé",
                "description": "Test permissions",
                "dateDebut": "2025-02-15T10:00:00",
                "dateFin": "2025-02-15T12:00:00",
                "lieu": "Lieu test",
                "typeEvenement": "FORMATION"
            }
            """;

        given()
            .contentType(ContentType.JSON)
            .body(nouvelEvenement)
            .when()
            .post("/api/evenements")
            .then()
            .statusCode(403);
    }

    @Test
    @Order(7)
    @DisplayName("PUT /api/evenements/{id} - Mettre à jour événement")
    @TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
    void testMettreAJourEvenement_Admin() {
        String evenementModifie = String.format("""
            {
                "titre": "Conférence API Test - Modifiée",
                "description": "Description mise à jour",
                "dateDebut": "%s",
                "dateFin": "%s",
                "lieu": "Nouveau lieu",
                "typeEvenement": "CONFERENCE",
                "capaciteMax": 75,
                "prix": 25.00,
                "inscriptionRequise": true,
                "visiblePublic": true
            }
            """,
            LocalDateTime.now().plusDays(16).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
            LocalDateTime.now().plusDays(16).plusHours(3).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        );

        given()
            .pathParam("id", evenementTestId)
            .contentType(ContentType.JSON)
            .body(evenementModifie)
            .when()
            .put("/api/evenements/{id}")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("titre", equalTo("Conférence API Test - Modifiée"))
            .body("description", equalTo("Description mise à jour"))
            .body("lieu", equalTo("Nouveau lieu"))
            .body("capaciteMax", equalTo(75))
            .body("prix", equalTo(25.0f));
    }

    @Test
    @Order(8)
    @DisplayName("GET /api/evenements/a-venir - Événements à venir")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testEvenementsAVenir() {
        given()
            .queryParam("page", 0)
            .queryParam("size", 10)
            .when()
            .get("/api/evenements/a-venir")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("size()", greaterThanOrEqualTo(0));
    }

    @Test
    @Order(9)
    @DisplayName("GET /api/evenements/publics - Événements publics (non authentifié)")
    void testEvenementsPublics_NonAuthentifie() {
        given()
            .queryParam("page", 0)
            .queryParam("size", 20)
            .when()
            .get("/api/evenements/publics")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("size()", greaterThanOrEqualTo(0));
    }

    @Test
    @Order(10)
    @DisplayName("GET /api/evenements/recherche - Recherche d'événements")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testRechercherEvenements() {
        given()
            .queryParam("q", "Conférence")
            .queryParam("page", 0)
            .queryParam("size", 20)
            .when()
            .get("/api/evenements/recherche")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("size()", greaterThanOrEqualTo(0));
    }

    @Test
    @Order(11)
    @DisplayName("GET /api/evenements/recherche - Terme de recherche manquant")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testRechercherEvenements_TermeManquant() {
        given()
            .queryParam("page", 0)
            .queryParam("size", 20)
            .when()
            .get("/api/evenements/recherche")
            .then()
            .statusCode(400)
            .body("error", equalTo("Le terme de recherche est obligatoire"));
    }

    @Test
    @Order(12)
    @DisplayName("GET /api/evenements/type/{type} - Événements par type")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testEvenementsParType() {
        given()
            .pathParam("type", "CONFERENCE")
            .queryParam("page", 0)
            .queryParam("size", 20)
            .when()
            .get("/api/evenements/type/{type}")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("size()", greaterThanOrEqualTo(0));
    }

    @Test
    @Order(13)
    @DisplayName("PATCH /api/evenements/{id}/statut - Changer statut")
    @TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
    void testChangerStatut() {
        given()
            .pathParam("id", evenementTestId)
            .queryParam("statut", "CONFIRME")
            .when()
            .patch("/api/evenements/{id}/statut")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("statut", equalTo("CONFIRME"));
    }

    @Test
    @Order(14)
    @DisplayName("GET /api/evenements/statistiques - Statistiques")
    @TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
    void testObtenirStatistiques() {
        given()
            .when()
            .get("/api/evenements/statistiques")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON)
            .body("total", notNullValue())
            .body("actifs", notNullValue())
            .body("timestamp", notNullValue());
    }

    @Test
    @Order(15)
    @DisplayName("DELETE /api/evenements/{id} - Supprimer événement")
    @TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
    void testSupprimerEvenement() {
        given()
            .pathParam("id", evenementTestId)
            .when()
            .delete("/api/evenements/{id}")
            .then()
            .statusCode(204);
    }

    @Test
    @Order(16)
    @DisplayName("Pagination - Paramètres valides")
    @TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
    void testPagination() {
        given()
            .queryParam("page", 0)
            .queryParam("size", 5)
            .queryParam("sort", "titre")
            .queryParam("direction", "asc")
            .when()
            .get("/api/evenements")
            .then()
            .statusCode(200)
            .contentType(ContentType.JSON);
    }
}
