package dev.lions.unionflow.server.resource;

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

import dev.lions.unionflow.server.api.dto.versement.response.VersementGatewayResponse;
import dev.lions.unionflow.server.api.dto.versement.response.VersementResponse;
import dev.lions.unionflow.server.api.dto.versement.response.VersementStatutResponse;
import dev.lions.unionflow.server.api.dto.versement.response.VersementSummaryResponse;
import dev.lions.unionflow.server.service.VersementService;
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.Collections;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

/**
 * Tests d'intégration REST pour {@link VersementResource}.
 *
 * @author UnionFlow Team
 * @version 4.0
 * @since 2026-04-13
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class VersementResourceTest {

    private static final String BASE = "/api/versements";
    private static final String VERSEMENT_ID = "00000000-0000-0000-0000-000000000010";
    private static final String MEMBRE_ID    = "00000000-0000-0000-0000-000000000020";
    private static final String INTENTION_ID = "00000000-0000-0000-0000-000000000030";

    @InjectMock
    VersementService versementService;

    // ── GET /{id} ─────────────────────────────────────────────────────────────

    @Test
    @Order(1)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void trouverParId_found_returns200() {
        VersementResponse response = VersementResponse.builder()
            .numeroReference("VRS-2026-001")
            .build();
        when(versementService.trouverParId(any(UUID.class))).thenReturn(response);

        given()
            .when().get(BASE + "/" + VERSEMENT_ID)
            .then().statusCode(200)
            .body("numeroReference", equalTo("VRS-2026-001"));
    }

    @Test
    @Order(2)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void trouverParId_notFound_returns404() {
        when(versementService.trouverParId(any(UUID.class)))
            .thenThrow(new NotFoundException("non trouvé"));

        given()
            .when().get(BASE + "/" + VERSEMENT_ID)
            .then().statusCode(404);
    }

    // ── GET /reference/{ref} ──────────────────────────────────────────────────

    @Test
    @Order(3)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void trouverParReference_found_returns200() {
        VersementResponse response = VersementResponse.builder()
            .numeroReference("VRS-2026-001")
            .build();
        when(versementService.trouverParNumeroReference(anyString())).thenReturn(response);

        given()
            .when().get(BASE + "/reference/VRS-2026-001")
            .then().statusCode(200);
    }

    // ── GET /membre/{id} ──────────────────────────────────────────────────────

    @Test
    @Order(4)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void listerParMembre_returns200() {
        when(versementService.listerParMembre(any(UUID.class)))
            .thenReturn(List.of(new VersementSummaryResponse()));

        given()
            .when().get(BASE + "/membre/" + MEMBRE_ID)
            .then().statusCode(200);
    }

    // ── GET /mes-versements ────────────────────────────────────────────────────

    @Test
    @Order(5)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void getMesVersements_returns200() {
        when(versementService.getMesVersements(anyInt()))
            .thenReturn(Collections.emptyList());

        given()
            .when().get(BASE + "/mes-versements")
            .then().statusCode(200);
    }

    @Test
    @Order(6)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void getMesVersements_customLimit_returns200() {
        when(versementService.getMesVersements(10))
            .thenReturn(Collections.emptyList());

        given()
            .queryParam("limit", 10)
            .when().get(BASE + "/mes-versements")
            .then().statusCode(200);
    }

    // ── POST /{id}/valider ────────────────────────────────────────────────────

    @Test
    @Order(7)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void validerVersement_returns200() {
        VersementResponse response = VersementResponse.builder()
            .statutPaiement("CONFIRME")
            .build();
        when(versementService.validerVersement(any(UUID.class))).thenReturn(response);

        given()
            .contentType(ContentType.JSON)
            .when().post(BASE + "/" + VERSEMENT_ID + "/valider")
            .then().statusCode(200)
            .body("statutPaiement", equalTo("CONFIRME"));
    }

    @Test
    @Order(8)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void validerVersement_notFound_returns404() {
        when(versementService.validerVersement(any(UUID.class)))
            .thenThrow(new NotFoundException("non trouvé"));

        given()
            .contentType(ContentType.JSON)
            .when().post(BASE + "/" + VERSEMENT_ID + "/valider")
            .then().statusCode(404);
    }

    // ── POST /{id}/annuler ────────────────────────────────────────────────────

    @Test
    @Order(9)
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void annulerVersement_returns200() {
        VersementResponse response = VersementResponse.builder()
            .statutPaiement("ANNULE")
            .build();
        when(versementService.annulerVersement(any(UUID.class))).thenReturn(response);

        given()
            .contentType(ContentType.JSON)
            .when().post(BASE + "/" + VERSEMENT_ID + "/annuler")
            .then().statusCode(200);
    }

    // ── POST /initier-wave ────────────────────────────────────────────────────

    @Test
    @Order(10)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void initierWave_validRequest_returns201() {
        VersementGatewayResponse gateway = VersementGatewayResponse.builder()
            .versementId(UUID.fromString(VERSEMENT_ID))
            .waveLaunchUrl("wave://checkout/abc123")
            .waveCheckoutSessionId("cos-abc")
            .clientReference(INTENTION_ID)
            .montant(new BigDecimal("5000"))
            .statut("EN_ATTENTE")
            .build();
        when(versementService.initierVersementWave(any())).thenReturn(gateway);

        given()
            .contentType(ContentType.JSON)
            .body("{\"cotisationId\":\"" + UUID.randomUUID() + "\",\"numeroTelephone\":\"771234567\"}")
            .when().post(BASE + "/initier-wave")
            .then().statusCode(201)
            .body("waveLaunchUrl", equalTo("wave://checkout/abc123"))
            .body("waveCheckoutSessionId", equalTo("cos-abc"));
    }

    @Test
    @Order(11)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void initierWave_missingCotisationId_returns400() {
        given()
            .contentType(ContentType.JSON)
            .body("{\"numeroTelephone\":\"771234567\"}")
            .when().post(BASE + "/initier-wave")
            .then().statusCode(400);
    }

    // ── GET /statut/{intentionId} ─────────────────────────────────────────────

    @Test
    @Order(12)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void getStatutVersement_completee_returns200() {
        VersementStatutResponse statut = VersementStatutResponse.builder()
            .intentionId(UUID.fromString(INTENTION_ID))
            .statut("COMPLETEE")
            .confirme(true)
            .message("Versement confirmé !")
            .build();
        when(versementService.verifierStatutVersement(any(UUID.class))).thenReturn(statut);

        given()
            .when().get(BASE + "/statut/" + INTENTION_ID)
            .then().statusCode(200)
            .body("confirme", equalTo(true))
            .body("statut", equalTo("COMPLETEE"));
    }

    @Test
    @Order(13)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void getStatutVersement_notFound_returns404() {
        when(versementService.verifierStatutVersement(any(UUID.class)))
            .thenThrow(new NotFoundException("non trouvé"));

        given()
            .when().get(BASE + "/statut/" + INTENTION_ID)
            .then().statusCode(404);
    }

    // ── POST /declarer-manuel ─────────────────────────────────────────────────

    @Test
    @Order(14)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void declarerManuel_validRequest_returns201() {
        VersementResponse response = VersementResponse.builder()
            .statutPaiement("EN_ATTENTE_VALIDATION")
            .methodePaiement("ESPECES")
            .build();
        when(versementService.declarerVersementManuel(any())).thenReturn(response);

        given()
            .contentType(ContentType.JSON)
            .body("{\"cotisationId\":\"" + UUID.randomUUID()
                + "\",\"methodePaiement\":\"ESPECES\"}")
            .when().post(BASE + "/declarer-manuel")
            .then().statusCode(201)
            .body("statutPaiement", equalTo("EN_ATTENTE_VALIDATION"));
    }

    @Test
    @Order(15)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void declarerManuel_methodInvalide_returns400() {
        given()
            .contentType(ContentType.JSON)
            .body("{\"cotisationId\":\"" + UUID.randomUUID()
                + "\",\"methodePaiement\":\"PAYPAL\"}")
            .when().post(BASE + "/declarer-manuel")
            .then().statusCode(400);
    }

    // ── POST /initier-depot-epargne ───────────────────────────────────────────

    @Test
    @Order(16)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void initierDepotEpargne_validRequest_returns201() {
        VersementGatewayResponse gateway = VersementGatewayResponse.builder()
            .waveLaunchUrl("wave://checkout/epargne")
            .statut("EN_ATTENTE")
            .montant(new BigDecimal("10000"))
            .build();
        when(versementService.initierDepotEpargneEnLigne(any())).thenReturn(gateway);

        given()
            .contentType(ContentType.JSON)
            .body("{\"compteId\":\"" + UUID.randomUUID()
                + "\",\"montant\":10000,\"numeroTelephone\":\"771234567\"}")
            .when().post(BASE + "/initier-depot-epargne")
            .then().statusCode(201)
            .body("waveLaunchUrl", notNullValue());
    }

    @Test
    @Order(17)
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    void initierDepotEpargne_montantManquant_returns400() {
        given()
            .contentType(ContentType.JSON)
            .body("{\"compteId\":\"" + UUID.randomUUID() + "\"}")
            .when().post(BASE + "/initier-depot-epargne")
            .then().statusCode(400);
    }
}
