package dev.lions.unionflow.server.integration;

import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.test.InjectMock;
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 dev.lions.unionflow.server.service.MembreKeycloakSyncService;
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 complet d'un membre
 * 
 * Scénario testé :
 * 1. Création d'une organisation (prérequis)
 * 2. Inscription d'un nouveau membre
 * 3. Consultation du profil
 * 4. Modification des informations
 * 5. Recherche du membre
 * 6. Suspension du membre
 * 
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-01-04
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MembreWorkflowIntegrationTest {

        private static final String BASE_PATH = "/api/membres";
        private static final String ORG_PATH = "/api/organisations";

        @Inject
        MembreRepository membreRepository;

        @Inject
        OrganisationRepository organisationRepository;

        @InjectMock
        MembreKeycloakSyncService keycloakSyncService;

        private UUID organisationId;
        private UUID membreId;
        private String membreEmail;

        @BeforeAll
        @Transactional
        void setupOrganisation() {
                // Créer l'organisation de test
                Organisation org = Organisation.builder()
                                .nom("Organisation Test E2E Membre")
                                .typeOrganisation("ASSOCIATION")
                                .statut("ACTIVE")
                                .email("org-membre-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-Membre-01: Inscrire un nouveau membre")
        void test01_InscrireMembre() {
                membreEmail = "membre-e2e-" + System.currentTimeMillis() + "@test.com";

                String membreJson = String.format("""
                                {
                                  "numeroMembre": "UF-E2E-%d",
                                  "nom": "Test",
                                  "prenom": "Membre",
                                  "email": "%s",
                                  "telephone": "+221701234567",
                                  "dateNaissance": "%s",
                                  "dateAdhesion": "%s",
                                  "associationId": "%s"
                                }
                                """,
                                System.currentTimeMillis(),
                                membreEmail,
                                LocalDate.of(1990, 1, 1).toString(),
                                LocalDate.now().toString(),
                                organisationId.toString());

                String idStr = given()
                                .contentType(ContentType.JSON)
                                .body(membreJson)
                                .when()
                                .post(BASE_PATH)
                                .then()
                                .statusCode(201)
                                .contentType(ContentType.JSON)
                                .body("nom", equalTo("Test"))
                                .body("prenom", equalTo("Membre"))
                                .body("email", equalTo(membreEmail))
                                .body("id", notNullValue())
                                .extract()
                                .path("id");

                membreId = UUID.fromString(idStr);

                // Activer le membre (créé avec actif=false en attente de validation)
                given()
                                .pathParam("id", membreId)
                                .when()
                                .put(BASE_PATH + "/{id}/activer")
                                .then()
                                .statusCode(200);
        }

        @Test
        @Order(2)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Membre-02: Consulter le profil du membre")
        void test02_ConsulterMembre() {
                given()
                                .pathParam("id", membreId)
                                .when()
                                .get(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(200)
                                .body("id", equalTo(membreId.toString()))
                                .body("nom", equalTo("Test"))
                                .body("email", equalTo(membreEmail));
        }

        @Test
        @Order(3)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Membre-03: Modifier les informations du membre")
        void test03_ModifierMembre() {
                String updateJson = String.format("""
                                {
                                  "numeroMembre": "UF-E2E-%d",
                                  "nom": "Test Modifié",
                                  "prenom": "Membre Modifié",
                                  "email": "%s",
                                  "telephone": "+221709876543",
                                  "dateNaissance": "%s",
                                  "dateAdhesion": "%s",
                                  "associationId": "%s"
                                }
                                """,
                                System.currentTimeMillis(),
                                membreEmail,
                                LocalDate.of(1990, 1, 1).toString(),
                                LocalDate.now().toString(),
                                organisationId.toString());

                given()
                                .contentType(ContentType.JSON)
                                .pathParam("id", membreId)
                                .body(updateJson)
                                .when()
                                .put(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(200)
                                .body("nom", equalTo("Test Modifié"))
                                .body("prenom", equalTo("Membre Modifié"));
        }

        @Test
        @Order(4)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Membre-04: Lister tous les membres (vérifier présence)")
        void test04_ListerMembres() {
                given()
                                .queryParam("page", 0)
                                .queryParam("size", 100)
                                .when()
                                .get(BASE_PATH)
                                .then()
                                .statusCode(200)
                                .body("data", hasSize(greaterThan(0)));
        }

        @Test
        @Order(5)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Membre-05: Supprimer le membre")
        void test05_SupprimerMembre() {
                given()
                                .pathParam("id", membreId)
                                .when()
                                .delete(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(204);

                // Vérifier qu'il est supprimé
                given()
                                .pathParam("id", membreId)
                                .when()
                                .get(BASE_PATH + "/{id}")
                                .then()
                                .statusCode(404);
        }

        @AfterAll
        @Transactional
        void cleanup() {
                if (membreId != null) {
                        membreRepository.findByIdOptional(membreId)
                                        .ifPresent(membre -> membreRepository.delete(membre));
                }
                if (organisationId != null) {
                        organisationRepository.findByIdOptional(organisationId)
                                        .ifPresent(org -> organisationRepository.delete(org));
                }
        }
}
