package dev.lions.unionflow.server.integration;

import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.EvenementRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;

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

/**
 * Tests d'intégration End-to-End pour le workflow des événements
 * 
 * Scénario testé :
 * 1. Création d'une organisation (prérequis)
 * 2. Création d'un événement
 * 3. Consultation des détails
 * 4. Modification de l'événement
 * 5. Liste des événements à venir
 * 6. Annulation de l'événement
 * 
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-01-04
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class EvenementWorkflowIntegrationTest {

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

        @Inject
        EvenementRepository evenementRepository;

        @Inject
        OrganisationRepository organisationRepository;

        private UUID organisationId;
        private UUID evenementId;

        @BeforeAll
        @Transactional
        void setupOrganisation() {
                Organisation org = Organisation.builder()
                                .nom("Organisation Test E2E Événements")
                                .typeOrganisation("ASSOCIATION")
                                .statut("ACTIVE")
                                .email("org-event-e2e-" + System.currentTimeMillis() + "@test.com")
                                .build();
                org.setDateCreation(LocalDateTime.now());
                org.setActif(true);
                organisationRepository.persist(org);
                organisationId = org.getId();
        }

        @Test
        @Order(1)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Event-01: Créer un nouvel événement")
        void test01_CreerEvenement() {
                LocalDate dateDebut = LocalDate.now().plusDays(7);
                String eventJson = String.format("""
                                {
                                  "titre": "Événement E2E Test",
                                  "description": "Description événement test end-to-end",
                                  "dateDebut": "%sT10:00:00",
                                  "lieu": "Dakar, Sénégal",
                                  "typeEvenement": "FORMATION",
                                  "statut": "PLANIFIE",
                                  "capaciteMax": 50,
                                  "prix": 5000.00,
                                  "organisation": { "id": "%s", "version": 0 },
                                  "inscriptionRequise": true,
                                  "visiblePublic": true,
                                  "actif": true
                                }
                                """,
                                dateDebut.toString(),
                                organisationId.toString());

                String idStr = given()
                                .contentType(ContentType.JSON)
                                .body(eventJson)
                                .when()
                                .post(BASE_PATH)
                                .then()
                                .statusCode(201)
                                .body("titre", equalTo("Événement E2E Test"))
                                .body("statut", equalTo("PLANIFIE"))
                                .body("id", notNullValue())
                                .extract()
                                .path("id");

                evenementId = UUID.fromString(idStr);
        }

        @Test
        @Order(2)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Event-02: Consulter les détails de l'événement")
        void test02_ConsulterEvenement() {
                given()
                                .pathParam("id", evenementId)
                                .when()
                                .get(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(200)
                                .body("id", equalTo(evenementId.toString()))
                                .body("titre", equalTo("Événement E2E Test"));
        }

        @Test
        @Order(3)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Event-03: Modifier l'événement")
        void test03_ModifierEvenement() {
                LocalDate dateDebut = LocalDate.now().plusDays(14);
                String updateJson = String.format("""
                                {
                                  "titre": "Événement E2E Test Modifié",
                                  "description": "Description modifiée",
                                  "dateDebut": "%sT14:00:00",
                                  "lieu": "Abidjan, Côte d'Ivoire",
                                  "typeEvenement": "FORMATION",
                                  "statut": "CONFIRME",
                                  "capaciteMax": 75,
                                  "prix": 7500.00,
                                  "organisation": { "id": "%s", "version": 0 },
                                  "inscriptionRequise": true,
                                  "visiblePublic": true,
                                  "actif": true
                                }
                                """,
                                dateDebut.toString(),
                                organisationId.toString());

                given()
                                .contentType(ContentType.JSON)
                                .pathParam("id", evenementId)
                                .body(updateJson)
                                .when()
                                .put(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(200)
                                .body("titre", equalTo("Événement E2E Test Modifié"))
                                .body("statut", equalTo("CONFIRME"))
                                .body("capaciteMax", equalTo(75));
        }

        @Test
        @Order(4)
        @TestSecurity(user = "membre@unionflow.com", roles = { "MEMBRE" })
        @DisplayName("E2E-Event-04: Lister tous les événements")
        void test04_ListerEvenements() {
                given()
                                .queryParam("page", 0)
                                .queryParam("size", 10)
                                .when()
                                .get(BASE_PATH)
                                .then()
                                .statusCode(200)
                                .body("data", notNullValue())
                                .body("data", hasSize(greaterThanOrEqualTo(1)));
        }

        @Test
        @Order(5)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Event-05: Supprimer l'événement")
        void test05_SupprimerEvenement() {
                given()
                                .pathParam("id", evenementId)
                                .when()
                                .delete(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(204);

                // Éviter la tentative de suppression dans cleanup
        }

        @AfterAll
        @Transactional
        void cleanup() {
                // Supprimer tous les événements de l'organisation de test d'abord
                if (organisationId != null) {
                        evenementRepository.getEntityManager()
                                        .createQuery("DELETE FROM Evenement e WHERE e.organisation.id = :orgId")
                                        .setParameter("orgId", organisationId)
                                        .executeUpdate();

                        organisationRepository.findByIdOptional(organisationId)
                                        .ifPresent(org -> organisationRepository.delete(org));
                }
        }
}
