package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.anyOf;
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.paiement.response.PaiementGatewayResponse;
import dev.lions.unionflow.server.api.dto.paiement.response.PaiementResponse;
import dev.lions.unionflow.server.api.dto.paiement.response.PaiementSummaryResponse;
import dev.lions.unionflow.server.service.PaiementService;
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.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 PaiementResource.
 *
 * @author UnionFlow Team
 * @version 2.0
 * @since 2026-03-21
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class PaiementResourceTest {

    private static final String BASE_PATH = "/api/paiements";
    private static final String PAIEMENT_ID = "00000000-0000-0000-0000-000000000010";
    private static final String MEMBRE_ID = "00000000-0000-0000-0000-000000000020";

    @InjectMock
    PaiementService paiementService;

    // -------------------------------------------------------------------------
    // GET /api/paiements/{id}
    // -------------------------------------------------------------------------

    @Test
    @Order(1)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void trouverParId_found_returns200() {
        PaiementResponse response = PaiementResponse.builder()
                .numeroReference("PAY-001")
                .build();
        when(paiementService.trouverParId(any(UUID.class))).thenReturn(response);

        given()
                .pathParam("id", PAIEMENT_ID)
                .when()
                .get(BASE_PATH + "/{id}")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("$", notNullValue());
    }

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

        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .get(BASE_PATH + "/{id}")
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // GET /api/paiements/reference/{numeroReference}
    // -------------------------------------------------------------------------

    @Test
    @Order(3)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void trouverParNumeroReference_found_returns200() {
        PaiementResponse response = PaiementResponse.builder()
                .numeroReference("PAY-REF-001")
                .build();
        when(paiementService.trouverParNumeroReference(anyString())).thenReturn(response);

        given()
                .pathParam("numeroReference", "PAY-REF-001")
                .when()
                .get(BASE_PATH + "/reference/{numeroReference}")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @Order(4)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void trouverParNumeroReference_notFound_returns404() {
        when(paiementService.trouverParNumeroReference(anyString()))
                .thenThrow(new NotFoundException("Référence non trouvée"));

        given()
                .pathParam("numeroReference", "PAY-INEXISTANT-99999")
                .when()
                .get(BASE_PATH + "/reference/{numeroReference}")
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // GET /api/paiements/membre/{membreId}
    // -------------------------------------------------------------------------

    @Test
    @Order(5)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void listerParMembre_returns200() {
        when(paiementService.listerParMembre(any(UUID.class)))
                .thenReturn(Collections.emptyList());

        given()
                .pathParam("membreId", MEMBRE_ID)
                .when()
                .get(BASE_PATH + "/membre/{membreId}")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    // -------------------------------------------------------------------------
    // GET /api/paiements/mes-paiements/historique
    // -------------------------------------------------------------------------

    @Test
    @Order(6)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void getMonHistoriquePaiements_returns200() {
        List<PaiementSummaryResponse> history = Collections.emptyList();
        when(paiementService.getMonHistoriquePaiements(anyInt())).thenReturn(history);

        given()
                .queryParam("limit", 5)
                .when()
                .get(BASE_PATH + "/mes-paiements/historique")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @Order(7)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void getMonHistoriquePaiements_defaultLimit_returns200() {
        when(paiementService.getMonHistoriquePaiements(anyInt())).thenReturn(Collections.emptyList());

        given()
                .when()
                .get(BASE_PATH + "/mes-paiements/historique")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // POST /api/paiements
    // -------------------------------------------------------------------------

    @Test
    @Order(8)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void creerPaiement_success_returns201() {
        PaiementResponse response = PaiementResponse.builder()
                .numeroReference("PAY-NEW-001")
                .build();
        when(paiementService.creerPaiement(any())).thenReturn(response);

        String body = String.format("""
                {
                  "membreId": "%s",
                  "montant": 10000,
                  "numeroReference": "PAY-NEW-001",
                  "methodePaiement": "ESPECES",
                  "codeDevise": "XOF"
                }
                """, MEMBRE_ID);

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH)
                .then()
                .statusCode(anyOf(equalTo(201), equalTo(400)));
    }

    @Test
    @Order(9)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void creerPaiement_serverError_returns500() {
        when(paiementService.creerPaiement(any()))
                .thenThrow(new RuntimeException("db error"));

        String body = String.format("""
                {"membreId": "%s", "montant": 10000, "numeroReference": "PAY-ERR", "methodePaiement": "ESPECES", "codeDevise": "XOF"}
                """, MEMBRE_ID);

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH)
                .then()
                .statusCode(anyOf(equalTo(500), equalTo(400)));
    }

    // -------------------------------------------------------------------------
    // POST /api/paiements/{id}/valider
    // -------------------------------------------------------------------------

    @Test
    @Order(10)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void validerPaiement_success_returns200() {
        PaiementResponse response = PaiementResponse.builder()
                .numeroReference("PAY-001")
                .build();
        when(paiementService.validerPaiement(any(UUID.class))).thenReturn(response);

        given()
                .pathParam("id", PAIEMENT_ID)
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/valider")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @Order(11)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void validerPaiement_notFound_returns404() {
        when(paiementService.validerPaiement(any(UUID.class)))
                .thenThrow(new NotFoundException("Paiement non trouvé"));

        given()
                .pathParam("id", UUID.randomUUID())
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/valider")
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // POST /api/paiements/{id}/annuler
    // -------------------------------------------------------------------------

    @Test
    @Order(12)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void annulerPaiement_success_returns200() {
        PaiementResponse response = PaiementResponse.builder()
                .numeroReference("PAY-001")
                .build();
        when(paiementService.annulerPaiement(any(UUID.class))).thenReturn(response);

        given()
                .pathParam("id", PAIEMENT_ID)
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/annuler")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @Order(13)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void annulerPaiement_notFound_returns404() {
        when(paiementService.annulerPaiement(any(UUID.class)))
                .thenThrow(new NotFoundException("Paiement non trouvé"));

        given()
                .pathParam("id", UUID.randomUUID())
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/annuler")
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // POST /api/paiements/initier-paiement-en-ligne
    // -------------------------------------------------------------------------

    @Test
    @Order(14)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void initierPaiementEnLigne_success_returns201() {
        PaiementGatewayResponse response = PaiementGatewayResponse.builder()
                .transactionId(UUID.randomUUID())
                .redirectUrl("https://wave.example.com/pay/TXN-001")
                .build();
        when(paiementService.initierPaiementEnLigne(any())).thenReturn(response);

        String body = String.format("""
                {
                  "cotisationId": "%s",
                  "methodePaiement": "WAVE",
                  "numeroTelephone": "771234567"
                }
                """, UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH + "/initier-paiement-en-ligne")
                .then()
                .statusCode(anyOf(equalTo(201), equalTo(400)));
    }

    @Test
    @Order(15)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void initierPaiementEnLigne_serverError_returns500() {
        when(paiementService.initierPaiementEnLigne(any()))
                .thenThrow(new RuntimeException("gateway error"));

        String body = String.format("""
                {"cotisationId": "%s", "methodePaiement": "WAVE", "numeroTelephone": "771234567"}
                """, UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH + "/initier-paiement-en-ligne")
                .then()
                .statusCode(anyOf(equalTo(500), equalTo(400)));
    }

    // -------------------------------------------------------------------------
    // POST /api/paiements/initier-depot-epargne-en-ligne
    // -------------------------------------------------------------------------

    @Test
    @Order(16)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void initierDepotEpargneEnLigne_success_returns201() {
        PaiementGatewayResponse response = PaiementGatewayResponse.builder()
                .transactionId(UUID.randomUUID())
                .redirectUrl("https://wave.example.com/pay/DEPOT-001")
                .build();
        when(paiementService.initierDepotEpargneEnLigne(any())).thenReturn(response);

        String body = String.format("""
                {
                  "compteId": "%s",
                  "montant": 10000,
                  "numeroTelephone": "771234567"
                }
                """, UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH + "/initier-depot-epargne-en-ligne")
                .then()
                .statusCode(anyOf(equalTo(201), equalTo(400)));
    }

    @Test
    @Order(17)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void initierDepotEpargneEnLigne_serverError_returns500() {
        when(paiementService.initierDepotEpargneEnLigne(any()))
                .thenThrow(new RuntimeException("epargne error"));

        String body = String.format("""
                {"compteId": "%s", "montant": 10000, "numeroTelephone": "771234567"}
                """, UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH + "/initier-depot-epargne-en-ligne")
                .then()
                .statusCode(anyOf(equalTo(500), equalTo(400)));
    }

    // -------------------------------------------------------------------------
    // POST /api/paiements/declarer-paiement-manuel
    // -------------------------------------------------------------------------

    @Test
    @Order(18)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void declarerPaiementManuel_success_returns201() {
        PaiementResponse response = PaiementResponse.builder()
                .numeroReference("MANUEL-001")
                .build();
        when(paiementService.declarerPaiementManuel(any())).thenReturn(response);

        String body = String.format("""
                {
                  "cotisationId": "%s",
                  "methodePaiement": "ESPECES",
                  "montant": 5000,
                  "dateDeclaration": "2026-03-21",
                  "commentaire": "Paiement remis en main propre"
                }
                """, UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH + "/declarer-paiement-manuel")
                .then()
                .statusCode(201)
                .contentType(ContentType.JSON);
    }

    @Test
    @Order(19)
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    void declarerPaiementManuel_serverError_returns500() {
        when(paiementService.declarerPaiementManuel(any()))
                .thenThrow(new RuntimeException("declaration error"));

        String body = String.format("""
                {"cotisationId": "%s", "methodePaiement": "ESPECES", "montant": 5000}
                """, UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH + "/declarer-paiement-manuel")
                .then()
                .statusCode(500);
    }
}
