package dev.lions.unionflow.server.resource.mutuelle.credit;

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.isNull;
import static org.mockito.Mockito.when;

import io.restassured.http.ContentType;

import dev.lions.unionflow.server.api.dto.mutuelle.credit.DemandeCreditResponse;
import dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutDemandeCredit;
import dev.lions.unionflow.server.service.mutuelle.credit.DemandeCreditService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests mock pour DemandeCreditResource — couvre la méthode approuver.
 */
@QuarkusTest
@DisplayName("DemandeCreditResource (mock)")
class DemandeCreditMockResourceTest {

    @InjectMock
    DemandeCreditService demandeCreditService;

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
    @DisplayName("POST /{id}/approbation — approuver retourne 200")
    void approuver_success_returns200() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.approuver(any(UUID.class), any(BigDecimal.class), any(Integer.class),
                any(BigDecimal.class), anyString())).thenReturn(response);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .queryParam("montant", "50000")
                .queryParam("duree", "12")
                .queryParam("taux", "5.0")
                .queryParam("notes", "Approuvé")
                .when()
                .post("/api/v1/mutuelle/credits/{id}/approbation")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
    @DisplayName("PATCH /{id}/statut — changerStatut avec statut valide retourne 200")
    void changerStatut_avecStatutValide_returns200() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.changerStatut(any(UUID.class), any(StatutDemandeCredit.class), anyString()))
                .thenReturn(response);

        given()
                .pathParam("id", UUID.randomUUID())
                .queryParam("statut", "EN_EVALUATION")
                .queryParam("notes", "Passage en évaluation")
                .when()
                .patch("/api/v1/mutuelle/credits/{id}/statut")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
    @DisplayName("PATCH /{id}/statut — changerStatut sans notes (null) retourne 200")
    void changerStatut_sansNotes_returns200() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.changerStatut(any(UUID.class), any(StatutDemandeCredit.class), isNull()))
                .thenReturn(response);

        given()
                .pathParam("id", UUID.randomUUID())
                .queryParam("statut", "REJETEE")
                .when()
                .patch("/api/v1/mutuelle/credits/{id}/statut")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
    @DisplayName("POST /{id}/decaissement — decaisser retourne 200")
    void decaisser_success_returns200() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.decaisser(any(UUID.class), any(LocalDate.class)))
                .thenReturn(response);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .queryParam("datePremiereEcheance", "2026-05-01")
                .when()
                .post("/api/v1/mutuelle/credits/{id}/decaissement")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE"})
    @DisplayName("GET /{id} — getDemandeById retourne 200 avec mock")
    void getDemandeById_avecMock_returns200() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.getDemandeById(any(UUID.class))).thenReturn(response);

        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .get("/api/v1/mutuelle/credits/{id}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE"})
    @DisplayName("GET /membre/{membreId} — getDemandesByMembre retourne 200 avec mock")
    void getDemandesByMembre_avecMock_returns200() {
        when(demandeCreditService.getDemandesByMembre(any(UUID.class))).thenReturn(List.of());

        given()
                .pathParam("membreId", UUID.randomUUID())
                .when()
                .get("/api/v1/mutuelle/credits/membre/{membreId}")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST / — soumettreDemande retourne 201 avec mock")
    void soumettreDemande_avecMock_returns201() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.soumettreDemande(any())).thenReturn(response);

        String body = """
                {
                  "membreId": "%s",
                  "typeCredit": "CONSOMMATION",
                  "montantDemande": 25000,
                  "dureeMois": 12,
                  "justificationDetaillee": "Besoin de financement pour matériel professionnel"
                }
                """.formatted(UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/v1/mutuelle/credits")
                .then()
                .statusCode(201);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
    @DisplayName("POST /{id}/approbation avec notes=null retourne 200")
    void approuver_sansNotes_returns200() {
        DemandeCreditResponse response = DemandeCreditResponse.builder().build();
        when(demandeCreditService.approuver(any(UUID.class), any(BigDecimal.class), any(Integer.class),
                any(BigDecimal.class), isNull())).thenReturn(response);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .queryParam("montant", "30000")
                .queryParam("duree", "24")
                .queryParam("taux", "8.5")
                .when()
                .post("/api/v1/mutuelle/credits/{id}/approbation")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // Error cases
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("POST /api/v1/mutuelle/credits sans authentification retourne 401")
    void soumettreDemande_sansAuthentification_returns401() {
        String body = """
                {
                  "membreId": "%s",
                  "typeCredit": "CONSOMMATION",
                  "montantDemande": 25000,
                  "dureeMois": 12,
                  "justificationDetaillee": "Besoin urgent"
                }
                """.formatted(UUID.randomUUID());

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/v1/mutuelle/credits")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"})
    @DisplayName("POST /{id}/approbation avec rôle MEMBRE retourne 403")
    void approuver_avecRoleMembre_returns403() {
        given()
                .contentType(ContentType.JSON)
                .pathParam("id", UUID.randomUUID())
                .queryParam("montant", "50000")
                .queryParam("duree", "12")
                .queryParam("taux", "5.0")
                .when()
                .post("/api/v1/mutuelle/credits/{id}/approbation")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
    @DisplayName("GET /{id} — service lève exception retourne 500")
    void getDemandeById_serviceException_returns500() {
        when(demandeCreditService.getDemandeById(any(UUID.class)))
                .thenThrow(new RuntimeException("Crédit introuvable"));

        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .get("/api/v1/mutuelle/credits/{id}")
                .then()
                .statusCode(500);
    }
}
