package dev.lions.unionflow.server.integration;

import dev.lions.unionflow.server.entity.Cotisation;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
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.math.BigDecimal;
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 cotisations
 * 
 * Scénario testé :
 * 1. Création d'une organisation et d'un membre (prérequis)
 * 2. Création d'une cotisation
 * 3. Enregistrement d'un paiement
 * 4. Consultation de l'historique
 * 5. Statistiques de cotisations
 * 
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-01-04
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CotisationWorkflowIntegrationTest {

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

        @Inject
        CotisationRepository cotisationRepository;

        @Inject
        MembreRepository membreRepository;

        @Inject
        OrganisationRepository organisationRepository;

        private UUID organisationId;
        private UUID membreId;
        private UUID cotisationId;

        @BeforeAll
        @Transactional
        void setupTestData() {
                // Créer organisation
                Organisation org = Organisation.builder()
                                .nom("Organisation Test E2E Cotisations")
                                .typeOrganisation("ASSOCIATION")
                                .statut("ACTIVE")
                                .email("org-cotisation-e2e-" + System.currentTimeMillis() + "@test.com")
                                .build();
                org.setDateCreation(LocalDateTime.now());
                org.setActif(true);
                organisationRepository.persist(org);
                organisationId = org.getId();

                // Créer membre
                Membre membre = Membre.builder()
                                .numeroMembre("UF-" + System.currentTimeMillis())
                                .nom("Test")
                                .prenom("Cotisation")
                                .email("membre-cot-e2e-" + System.currentTimeMillis() + "@test.com")
                                .telephone("+221701234567")
                                .dateNaissance(LocalDate.of(1990, 1, 1))
                                .build();
                membre.setDateCreation(LocalDateTime.now());
                membre.setActif(true);
                membreRepository.persist(membre);
                membreId = membre.getId();
        }

        @Test
        @Order(1)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Cotisation-01: Créer une cotisation pour le membre")
        void test01_CreerCotisation() {
                String cotisationJson = String.format("""
                                {
                                  "numeroReference": "COT-E2E-%d",
                                  "membreId": "%s",
                                  "organisationId": "%s",
                                  "typeCotisation": "MENSUELLE",
                                  "libelle": "Cotisation Mensuelle E2E",
                                  "montantDu": 5000.00,
                                  "codeDevise": "XOF",
                                  "mois": %d,
                                  "annee": %d,
                                  "dateEcheance": "%s",
                                  "statut": "EN_ATTENTE"
                                }
                                """,
                                System.currentTimeMillis(),
                                membreId.toString(),
                                organisationId.toString(),
                                LocalDate.now().getMonthValue(),
                                LocalDate.now().getYear(),
                                LocalDate.now().plusDays(30).toString());

                String idStr = given()
                                .contentType(ContentType.JSON)
                                .body(cotisationJson)
                                .when()
                                .post(BASE_PATH)
                                .then()
                                .statusCode(201)
                                .body("montantDu", equalTo(5000.0f))
                                .body("statut", equalTo("EN_ATTENTE"))
                                .body("id", notNullValue())
                                .extract()
                                .path("id");

                cotisationId = UUID.fromString(idStr);
        }

        @Test
        @Order(2)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Cotisation-02: Enregistrer un paiement")
        void test02_EnregistrerPaiement() {
                String paiementJson = String.format("""
                                {
                                  "montantPaye": 5000.00,
                                  "datePaiement": "%s",
                                  "modePaiement": "ESPECES",
                                  "reference": "PAY-E2E-%d"
                                }
                                """,
                                LocalDate.now().toString(),
                                System.currentTimeMillis());

                given()
                                .contentType(ContentType.JSON)
                                .pathParam("id", cotisationId)
                                .body(paiementJson)
                                .when()
                                .put(BASE_PATH + "/{id}/payer")
                                .then()
                                .statusCode(200)
                                .body("statut", equalTo("PAYEE"))
                                .body("montantPaye", equalTo(5000.0f));
        }

        @Test
        @Order(3)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Cotisation-03: Consulter l'historique du membre")
        void test03_ConsulterHistorique() {
                given()
                                .pathParam("membreId", membreId)
                                .when()
                                .get(BASE_PATH + "/membre/{membreId}")
                                .then()
                                .statusCode(200)
                                .body("$", hasSize(greaterThanOrEqualTo(1)))
                                .body("[0].id", equalTo(cotisationId.toString()))
                                .body("[0].statut", equalTo("PAYEE"));
        }

        @Test
        @Order(4)
        @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
        @DisplayName("E2E-Cotisation-04: Obtenir les statistiques")
        void test04_StatistiquesCotisations() {
                given()
                                .queryParam("annee", LocalDate.now().getYear())
                                .when()
                                .get(BASE_PATH + "/statistiques")
                                .then()
                                .statusCode(200)
                                .body("totalCotisations", greaterThanOrEqualTo(1))
                                .body("totalMontant", greaterThanOrEqualTo(5000.0f));
        }

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