package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.wave.CompteWaveDTO;
import dev.lions.unionflow.server.api.dto.wave.TransactionWaveDTO;
import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
import dev.lions.unionflow.server.service.WaveService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.ws.rs.NotFoundException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class WaveResourceTest {

    @InjectMock
    WaveService waveService;

    // ============================================================
    // COMPTES WAVE
    // ============================================================

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes retourne 201 avec données valides")
    void creerCompteWave_validRequest_returns201() {
        CompteWaveDTO created = new CompteWaveDTO();
        created.setId(UUID.randomUUID());
        created.setNumeroTelephone("771234567");
        created.setStatutCompte(StatutCompteWave.NON_VERIFIE);

        when(waveService.creerCompteWave(any(CompteWaveDTO.class))).thenReturn(created);

        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577123456",
                "statutCompte", "NON_VERIFIE",
                "environnement", "SANDBOX"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/comptes")
                .then()
                .statusCode(201)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes retourne 400 si numéro dupliqué")
    void creerCompteWave_duplicatePhone_returns400() {
        when(waveService.creerCompteWave(any(CompteWaveDTO.class)))
                .thenThrow(new IllegalArgumentException("Un compte Wave existe déjà pour ce numéro"));

        Map<String, Object> body = Map.of(
                "numeroTelephone", "771234567",
                "statutCompte", "NON_VERIFIE"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/comptes")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes retourne 400 si erreur générale")
    void creerCompteWave_generalError_returns400() {
        when(waveService.creerCompteWave(any(CompteWaveDTO.class)))
                .thenThrow(new RuntimeException("Erreur DB"));

        Map<String, Object> body = Map.of(
                "numeroTelephone", "779999999"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/comptes")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes retourne 400 via catch(Exception) avec numéro valide")
    void creerCompteWave_validPhone_genericException_returns400() {
        when(waveService.creerCompteWave(any(CompteWaveDTO.class)))
                .thenThrow(new RuntimeException("Erreur DB inattendue"));

        // Numéro valide (+225 + 8 chiffres) pour passer la validation @Pattern
        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577123456",
                "environnement", "SANDBOX"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/comptes")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Erreur lors de la création du compte Wave"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/comptes/{id} retourne 200 quand mis à jour")
    void mettreAJourCompteWave_found_returns200() {
        UUID id = UUID.randomUUID();
        CompteWaveDTO updated = new CompteWaveDTO();
        updated.setId(id);
        updated.setNumeroTelephone("771234567");
        updated.setStatutCompte(StatutCompteWave.VERIFIE);

        when(waveService.mettreAJourCompteWave(eq(id), any(CompteWaveDTO.class))).thenReturn(updated);

        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577123456",
                "statutCompte", "VERIFIE",
                "environnement", "PRODUCTION"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .pathParam("id", id)
                .when()
                .put("/api/wave/comptes/{id}")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/comptes/{id} retourne 404 quand non trouvé")
    void mettreAJourCompteWave_notFound_returns404() {
        UUID id = UUID.randomUUID();
        when(waveService.mettreAJourCompteWave(eq(id), any(CompteWaveDTO.class)))
                .thenThrow(new NotFoundException("Compte Wave non trouvé"));

        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577654321",
                "statutCompte", "VERIFIE"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .pathParam("id", id)
                .when()
                .put("/api/wave/comptes/{id}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/comptes/{id} retourne 400 si erreur générale")
    void mettreAJourCompteWave_generalError_returns400() {
        UUID id = UUID.randomUUID();
        when(waveService.mettreAJourCompteWave(eq(id), any(CompteWaveDTO.class)))
                .thenThrow(new RuntimeException("Erreur DB"));

        Map<String, Object> body = Map.of("statutCompte", "SUSPENDU");

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .pathParam("id", id)
                .when()
                .put("/api/wave/comptes/{id}")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/comptes/{id} retourne 400 via catch(Exception) avec numéro valide")
    void mettreAJourCompteWave_validPhone_genericException_returns400() {
        UUID id = UUID.randomUUID();
        when(waveService.mettreAJourCompteWave(eq(id), any(CompteWaveDTO.class)))
                .thenThrow(new RuntimeException("Erreur DB inattendue"));

        // Numéro valide (+225 + 8 chiffres) pour passer la validation @Pattern
        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577654321",
                "environnement", "PRODUCTION"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .pathParam("id", id)
                .when()
                .put("/api/wave/comptes/{id}")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Erreur lors de la mise à jour du compte Wave"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes/{id}/verifier retourne 200 quand vérifié")
    void verifierCompteWave_found_returns200() {
        UUID id = UUID.randomUUID();
        CompteWaveDTO verified = new CompteWaveDTO();
        verified.setId(id);
        verified.setStatutCompte(StatutCompteWave.VERIFIE);

        when(waveService.verifierCompteWave(id)).thenReturn(verified);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", id)
                .when()
                .post("/api/wave/comptes/{id}/verifier")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes/{id}/verifier retourne 404 quand non trouvé")
    void verifierCompteWave_notFound_returns404() {
        UUID id = UUID.randomUUID();
        when(waveService.verifierCompteWave(id))
                .thenThrow(new NotFoundException("Compte Wave non trouvé"));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", id)
                .when()
                .post("/api/wave/comptes/{id}/verifier")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes/{id}/verifier retourne 400 si erreur générale")
    void verifierCompteWave_generalError_returns400() {
        UUID id = UUID.randomUUID();
        when(waveService.verifierCompteWave(id))
                .thenThrow(new RuntimeException("Erreur de vérification"));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", id)
                .when()
                .post("/api/wave/comptes/{id}/verifier")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/{id} retourne 200 quand trouvé")
    void trouverCompteWaveParId_found_returns200() {
        UUID id = UUID.randomUUID();
        CompteWaveDTO compte = new CompteWaveDTO();
        compte.setId(id);
        compte.setNumeroTelephone("771234567");

        when(waveService.trouverCompteWaveParId(id)).thenReturn(compte);

        given()
                .pathParam("id", id)
                .when()
                .get("/api/wave/comptes/{id}")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/{id} retourne 404 quand non trouvé")
    void trouverCompteWaveParId_notFound_returns404() {
        UUID id = UUID.randomUUID();
        when(waveService.trouverCompteWaveParId(id))
                .thenThrow(new NotFoundException("Compte Wave non trouvé"));

        given()
                .pathParam("id", id)
                .when()
                .get("/api/wave/comptes/{id}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/{id} retourne 400 si erreur générale")
    void trouverCompteWaveParId_generalError_returns400() {
        UUID id = UUID.randomUUID();
        when(waveService.trouverCompteWaveParId(id))
                .thenThrow(new RuntimeException("Erreur DB"));

        given()
                .pathParam("id", id)
                .when()
                .get("/api/wave/comptes/{id}")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/telephone/{numero} retourne 200 quand trouvé")
    void trouverCompteWaveParTelephone_found_returns200() {
        CompteWaveDTO compte = new CompteWaveDTO();
        compte.setId(UUID.randomUUID());
        compte.setNumeroTelephone("771234567");

        when(waveService.trouverCompteWaveParTelephone("771234567")).thenReturn(compte);

        given()
                .pathParam("numeroTelephone", "771234567")
                .when()
                .get("/api/wave/comptes/telephone/{numeroTelephone}")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/telephone/{numero} retourne 404 quand null")
    void trouverCompteWaveParTelephone_nullResult_returns404() {
        when(waveService.trouverCompteWaveParTelephone("779999999")).thenReturn(null);

        given()
                .pathParam("numeroTelephone", "779999999")
                .when()
                .get("/api/wave/comptes/telephone/{numeroTelephone}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/telephone/{numero} retourne 400 si erreur générale")
    void trouverCompteWaveParTelephone_generalError_returns400() {
        when(waveService.trouverCompteWaveParTelephone(anyString()))
                .thenThrow(new RuntimeException("Erreur DB"));

        given()
                .pathParam("numeroTelephone", "771111111")
                .when()
                .get("/api/wave/comptes/telephone/{numeroTelephone}")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/organisation/{organisationId} retourne 200 avec liste")
    void listerComptesWaveParOrganisation_returns200() {
        UUID organisationId = UUID.randomUUID();
        CompteWaveDTO compte = new CompteWaveDTO();
        compte.setId(UUID.randomUUID());
        compte.setNumeroTelephone("771234567");

        when(waveService.listerComptesWaveParOrganisation(organisationId))
                .thenReturn(List.of(compte));

        given()
                .pathParam("organisationId", organisationId)
                .when()
                .get("/api/wave/comptes/organisation/{organisationId}")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/organisation/{organisationId} retourne 400 si erreur")
    void listerComptesWaveParOrganisation_serviceThrows_returns400() {
        UUID organisationId = UUID.randomUUID();
        when(waveService.listerComptesWaveParOrganisation(organisationId))
                .thenThrow(new RuntimeException("Erreur DB"));

        given()
                .pathParam("organisationId", organisationId)
                .when()
                .get("/api/wave/comptes/organisation/{organisationId}")
                .then()
                .statusCode(400);
    }

    // ============================================================
    // TRANSACTIONS WAVE
    // ============================================================

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/transactions retourne 201 avec données valides")
    void creerTransactionWave_validRequest_returns201() {
        TransactionWaveDTO created = new TransactionWaveDTO();
        created.setId(UUID.randomUUID());
        created.setWaveTransactionId("WAVE-TXN-001");
        created.setMontant(BigDecimal.valueOf(5000));
        created.setStatutTransaction(StatutTransactionWave.INITIALISE);

        when(waveService.creerTransactionWave(any(TransactionWaveDTO.class))).thenReturn(created);

        UUID compteWaveId = UUID.randomUUID();
        Map<String, Object> body = Map.of(
                "waveTransactionId", "WAVE-TXN-001",
                "montant", 5000,
                "typeTransaction", "PAIEMENT",
                "statutTransaction", "INITIALISE",
                "codeDevise", "XOF",
                "compteWaveId", compteWaveId.toString(),
                "telephonePayeur", "+22577123456",
                "telephoneBeneficiaire", "+22577777777"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/transactions")
                .then()
                .statusCode(201)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/transactions retourne 400 si erreur")
    void creerTransactionWave_serviceThrows_returns400() {
        when(waveService.creerTransactionWave(any(TransactionWaveDTO.class)))
                .thenThrow(new RuntimeException("Erreur de traitement"));

        Map<String, Object> body = Map.of(
                "montant", 5000
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/transactions")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/transactions retourne 400 via catch(Exception) avec body valide")
    void creerTransactionWave_validBody_genericException_returns400() {
        when(waveService.creerTransactionWave(any(TransactionWaveDTO.class)))
                .thenThrow(new RuntimeException("Erreur inattendue de traitement"));

        UUID compteWaveId = UUID.randomUUID();
        Map<String, Object> body = Map.of(
                "waveTransactionId", "WAVE-ERR-" + UUID.randomUUID().toString().substring(0, 8),
                "typeTransaction", "PAIEMENT",
                "statutTransaction", "INITIALISE",
                "montant", 1000,
                "codeDevise", "XOF",
                "compteWaveId", compteWaveId.toString()
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/transactions")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Erreur lors de la création de la transaction Wave"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/transactions/{waveTransactionId} retourne 200 quand trouvé")
    void trouverTransactionWaveParId_found_returns200() {
        String txnId = "WAVE-TXN-002";
        TransactionWaveDTO txn = new TransactionWaveDTO();
        txn.setId(UUID.randomUUID());
        txn.setWaveTransactionId(txnId);
        txn.setStatutTransaction(StatutTransactionWave.REUSSIE);

        when(waveService.trouverTransactionWaveParId(txnId)).thenReturn(txn);

        given()
                .pathParam("waveTransactionId", txnId)
                .when()
                .get("/api/wave/transactions/{waveTransactionId}")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/transactions/{waveTransactionId} retourne 404 quand non trouvé")
    void trouverTransactionWaveParId_notFound_returns404() {
        String txnId = "WAVE-UNKNOWN";
        when(waveService.trouverTransactionWaveParId(txnId))
                .thenThrow(new NotFoundException("Transaction Wave non trouvée"));

        given()
                .pathParam("waveTransactionId", txnId)
                .when()
                .get("/api/wave/transactions/{waveTransactionId}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/transactions/{waveTransactionId} retourne 400 si erreur générale")
    void trouverTransactionWaveParId_generalError_returns400() {
        String txnId = "WAVE-ERR";
        when(waveService.trouverTransactionWaveParId(txnId))
                .thenThrow(new RuntimeException("Erreur DB"));

        given()
                .pathParam("waveTransactionId", txnId)
                .when()
                .get("/api/wave/transactions/{waveTransactionId}")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/transactions/{waveTransactionId}/statut retourne 200 quand mis à jour")
    void mettreAJourStatutTransaction_found_returns200() {
        String txnId = "WAVE-TXN-003";
        TransactionWaveDTO updated = new TransactionWaveDTO();
        updated.setId(UUID.randomUUID());
        updated.setWaveTransactionId(txnId);
        updated.setStatutTransaction(StatutTransactionWave.REUSSIE);

        when(waveService.mettreAJourStatutTransaction(eq(txnId), any(StatutTransactionWave.class)))
                .thenReturn(updated);

        given()
                .contentType(ContentType.JSON)
                .body("\"REUSSIE\"")
                .pathParam("waveTransactionId", txnId)
                .when()
                .put("/api/wave/transactions/{waveTransactionId}/statut")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/transactions/{waveTransactionId}/statut retourne 404 quand non trouvé")
    void mettreAJourStatutTransaction_notFound_returns404() {
        String txnId = "WAVE-UNKNOWN";
        when(waveService.mettreAJourStatutTransaction(eq(txnId), any(StatutTransactionWave.class)))
                .thenThrow(new NotFoundException("Transaction Wave non trouvée"));

        given()
                .contentType(ContentType.JSON)
                .body("\"ECHOUE\"")
                .pathParam("waveTransactionId", txnId)
                .when()
                .put("/api/wave/transactions/{waveTransactionId}/statut")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/transactions/{waveTransactionId}/statut retourne 400 si erreur générale")
    void mettreAJourStatutTransaction_generalError_returns400() {
        String txnId = "WAVE-ERR";
        when(waveService.mettreAJourStatutTransaction(eq(txnId), any(StatutTransactionWave.class)))
                .thenThrow(new RuntimeException("Erreur DB"));

        given()
                .contentType(ContentType.JSON)
                .body("\"ANNULEE\"")
                .pathParam("waveTransactionId", txnId)
                .when()
                .put("/api/wave/transactions/{waveTransactionId}/statut")
                .then()
                .statusCode(400);
    }

    // ============================================================
    // TESTS ROLE MEMBRE — branches @RolesAllowed({"ADMIN","MEMBRE"})
    // ============================================================

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("POST /api/wave/comptes avec rôle MEMBRE retourne 201")
    void creerCompteWave_roleMembre_returns201() {
        CompteWaveDTO created = new CompteWaveDTO();
        created.setId(UUID.randomUUID());
        created.setNumeroTelephone("+22577000001");
        created.setStatutCompte(StatutCompteWave.NON_VERIFIE);

        when(waveService.creerCompteWave(any(CompteWaveDTO.class))).thenReturn(created);

        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577000001",
                "statutCompte", "NON_VERIFIE",
                "environnement", "SANDBOX"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/comptes")
                .then()
                .statusCode(201);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT /api/wave/comptes/{id} avec rôle MEMBRE retourne 200")
    void mettreAJourCompteWave_roleMembre_returns200() {
        UUID id = UUID.randomUUID();
        CompteWaveDTO updated = new CompteWaveDTO();
        updated.setId(id);
        updated.setNumeroTelephone("+22577000002");
        updated.setStatutCompte(StatutCompteWave.VERIFIE);

        when(waveService.mettreAJourCompteWave(eq(id), any(CompteWaveDTO.class))).thenReturn(updated);

        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577000002",
                "statutCompte", "VERIFIE",
                "environnement", "SANDBOX"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .pathParam("id", id)
                .when()
                .put("/api/wave/comptes/{id}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("POST /api/wave/comptes/{id}/verifier avec rôle MEMBRE retourne 200")
    void verifierCompteWave_roleMembre_returns200() {
        UUID id = UUID.randomUUID();
        CompteWaveDTO verified = new CompteWaveDTO();
        verified.setId(id);
        verified.setStatutCompte(StatutCompteWave.VERIFIE);

        when(waveService.verifierCompteWave(id)).thenReturn(verified);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", id)
                .when()
                .post("/api/wave/comptes/{id}/verifier")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("POST /api/wave/transactions avec rôle MEMBRE retourne 201")
    void creerTransactionWave_roleMembre_returns201() {
        TransactionWaveDTO created = new TransactionWaveDTO();
        created.setId(UUID.randomUUID());
        created.setWaveTransactionId("WAVE-MBR-001");
        created.setMontant(java.math.BigDecimal.valueOf(2000));
        created.setStatutTransaction(StatutTransactionWave.INITIALISE);

        when(waveService.creerTransactionWave(any(TransactionWaveDTO.class))).thenReturn(created);

        UUID compteWaveId = UUID.randomUUID();
        Map<String, Object> body = Map.of(
                "waveTransactionId", "WAVE-MBR-001",
                "montant", 2000,
                "typeTransaction", "PAIEMENT",
                "statutTransaction", "INITIALISE",
                "codeDevise", "XOF",
                "compteWaveId", compteWaveId.toString(),
                "telephonePayeur", "+22577000001",
                "telephoneBeneficiaire", "+22577000002"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/transactions")
                .then()
                .statusCode(201);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT /api/wave/transactions/{id}/statut avec rôle MEMBRE retourne 200")
    void mettreAJourStatutTransaction_roleMembre_returns200() {
        String txnId = "WAVE-MBR-TXN-001";
        TransactionWaveDTO updated = new TransactionWaveDTO();
        updated.setId(UUID.randomUUID());
        updated.setWaveTransactionId(txnId);
        updated.setStatutTransaction(StatutTransactionWave.REUSSIE);

        when(waveService.mettreAJourStatutTransaction(eq(txnId), any(StatutTransactionWave.class)))
                .thenReturn(updated);

        given()
                .contentType(ContentType.JSON)
                .body("\"REUSSIE\"")
                .pathParam("waveTransactionId", txnId)
                .when()
                .put("/api/wave/transactions/{waveTransactionId}/statut")
                .then()
                .statusCode(200);
    }

    // ============================================================
    // TESTS ROLE USER — GET endpoints accessibles à tous les rôles
    // ============================================================

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("GET /api/wave/comptes/{id} avec rôle USER retourne 200")
    void trouverCompteWaveParId_roleUser_returns200() {
        UUID id = UUID.randomUUID();
        CompteWaveDTO compte = new CompteWaveDTO();
        compte.setId(id);
        compte.setNumeroTelephone("+22577000003");

        when(waveService.trouverCompteWaveParId(id)).thenReturn(compte);

        given()
                .pathParam("id", id)
                .when()
                .get("/api/wave/comptes/{id}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("GET /api/wave/comptes/telephone/{numero} avec rôle USER retourne 200")
    void trouverCompteWaveParTelephone_roleUser_returns200() {
        CompteWaveDTO compte = new CompteWaveDTO();
        compte.setId(UUID.randomUUID());
        compte.setNumeroTelephone("+22577000004");

        when(waveService.trouverCompteWaveParTelephone("+22577000004")).thenReturn(compte);

        given()
                .pathParam("numeroTelephone", "+22577000004")
                .when()
                .get("/api/wave/comptes/telephone/{numeroTelephone}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("GET /api/wave/comptes/organisation/{organisationId} avec rôle USER retourne 200")
    void listerComptesWaveParOrganisation_roleUser_returns200() {
        UUID organisationId = UUID.randomUUID();
        when(waveService.listerComptesWaveParOrganisation(organisationId))
                .thenReturn(List.of());

        given()
                .pathParam("organisationId", organisationId)
                .when()
                .get("/api/wave/comptes/organisation/{organisationId}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("GET /api/wave/transactions/{waveTransactionId} avec rôle USER retourne 200")
    void trouverTransactionWaveParId_roleUser_returns200() {
        String txnId = "WAVE-USER-001";
        TransactionWaveDTO txn = new TransactionWaveDTO();
        txn.setId(UUID.randomUUID());
        txn.setWaveTransactionId(txnId);
        txn.setStatutTransaction(StatutTransactionWave.REUSSIE);

        when(waveService.trouverTransactionWaveParId(txnId)).thenReturn(txn);

        given()
                .pathParam("waveTransactionId", txnId)
                .when()
                .get("/api/wave/transactions/{waveTransactionId}")
                .then()
                .statusCode(200);
    }

    // ============================================================
    // TESTS AUTORISATION — rôle interdit (USER ne peut pas POST/PUT)
    // ============================================================

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("POST /api/wave/comptes avec rôle USER retourne 403")
    void creerCompteWave_roleUser_returns403() {
        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577999999",
                "statutCompte", "NON_VERIFIE"
        );

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

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("POST /api/wave/transactions avec rôle USER retourne 403")
    void creerTransactionWave_roleUser_returns403() {
        Map<String, Object> body = Map.of(
                "montant", 1000
        );

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

    // ============================================================
    // TESTS VALEURS LIMITES — ErrorResponse et cas null
    // ============================================================

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/organisation/{organisationId} retourne 200 avec liste vide")
    void listerComptesWaveParOrganisation_listeVide_returns200() {
        UUID organisationId = UUID.randomUUID();
        when(waveService.listerComptesWaveParOrganisation(organisationId))
                .thenReturn(List.of());

        given()
                .pathParam("organisationId", organisationId)
                .when()
                .get("/api/wave/comptes/organisation/{organisationId}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/comptes/{id} retourne 400 avec exception runtime et message non null")
    void mettreAJourCompteWave_runtimeExceptionAvecMessage_erreurDansCorps() {
        UUID id = UUID.randomUUID();
        when(waveService.mettreAJourCompteWave(eq(id), any(CompteWaveDTO.class)))
                .thenThrow(new RuntimeException("Message d'erreur précis"));

        Map<String, Object> body = Map.of("statutCompte", "VERIFIE");

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .pathParam("id", id)
                .when()
                .put("/api/wave/comptes/{id}")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes retourne 400 avec message d'erreur dans le corps")
    void creerCompteWave_illegalArgument_errorDansCorps() {
        when(waveService.creerCompteWave(any(CompteWaveDTO.class)))
                .thenThrow(new IllegalArgumentException("Numéro déjà utilisé"));

        Map<String, Object> body = Map.of(
                "numeroTelephone", "+22577111111",
                "statutCompte", "NON_VERIFIE"
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/comptes")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Numéro déjà utilisé"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/comptes/{id}/verifier retourne 400 avec message d'erreur dans le corps")
    void verifierCompteWave_runtimeException_errorDansCorps() {
        UUID id = UUID.randomUUID();
        when(waveService.verifierCompteWave(id))
                .thenThrow(new RuntimeException("Service Wave indisponible"));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", id)
                .when()
                .post("/api/wave/comptes/{id}/verifier")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Service Wave indisponible"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/{id} retourne 400 avec message d'erreur dans le corps")
    void trouverCompteWaveParId_runtimeException_errorDansCorps() {
        UUID id = UUID.randomUUID();
        when(waveService.trouverCompteWaveParId(id))
                .thenThrow(new RuntimeException("Connexion impossible"));

        given()
                .pathParam("id", id)
                .when()
                .get("/api/wave/comptes/{id}")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Connexion impossible"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/telephone/{numero} retourne 400 avec message d'erreur dans le corps")
    void trouverCompteWaveParTelephone_runtimeException_errorDansCorps() {
        when(waveService.trouverCompteWaveParTelephone(anyString()))
                .thenThrow(new RuntimeException("Timeout réseau"));

        given()
                .pathParam("numeroTelephone", "+22577222222")
                .when()
                .get("/api/wave/comptes/telephone/{numeroTelephone}")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Timeout réseau"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/wave/comptes/organisation/{organisationId} retourne 400 avec message d'erreur dans le corps")
    void listerComptesWaveParOrganisation_runtimeException_errorDansCorps() {
        UUID organisationId = UUID.randomUUID();
        when(waveService.listerComptesWaveParOrganisation(organisationId))
                .thenThrow(new RuntimeException("Base de données inaccessible"));

        given()
                .pathParam("organisationId", organisationId)
                .when()
                .get("/api/wave/comptes/organisation/{organisationId}")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Base de données inaccessible"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("POST /api/wave/transactions retourne 400 avec message d'erreur dans le corps")
    void creerTransactionWave_runtimeException_errorDansCorps() {
        when(waveService.creerTransactionWave(any(TransactionWaveDTO.class)))
                .thenThrow(new RuntimeException("Solde insuffisant"));

        Map<String, Object> body = Map.of(
                "montant", 999999
        );

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/wave/transactions")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT /api/wave/transactions/{id}/statut retourne 400 avec message d'erreur dans le corps")
    void mettreAJourStatutTransaction_runtimeException_errorDansCorps() {
        String txnId = "WAVE-ERR-MSG";
        when(waveService.mettreAJourStatutTransaction(eq(txnId), any(StatutTransactionWave.class)))
                .thenThrow(new RuntimeException("Transaction expirée"));

        given()
                .contentType(ContentType.JSON)
                .body("\"ECHOUE\"")
                .pathParam("waveTransactionId", txnId)
                .when()
                .put("/api/wave/transactions/{waveTransactionId}/statut")
                .then()
                .statusCode(400)
                .body("error", org.hamcrest.Matchers.containsString("Transaction expirée"));
    }
}
