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

import dev.lions.unionflow.server.api.dto.finance.request.CreateAdhesionRequest;
import dev.lions.unionflow.server.api.dto.finance.request.UpdateAdhesionRequest;
import dev.lions.unionflow.server.api.dto.finance.response.AdhesionResponse;
import dev.lions.unionflow.server.service.AdhesionService;
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.Map;
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 complémentaires avec @InjectMock pour AdhesionResource.
 * Couvre les chemins de succès non atteints par AdhesionResourceTest (DB réelle).
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-21
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class AdhesionMockResourceTest {

    private static final String BASE_PATH = "/api/adhesions";
    private static final String ADHESION_ID = "00000000-0000-0000-0000-000000000030";
    private static final String MEMBRE_ID = "00000000-0000-0000-0000-000000000031";
    private static final String ORG_ID = "00000000-0000-0000-0000-000000000032";

    @InjectMock
    AdhesionService adhesionService;

    private AdhesionResponse buildAdhesionResponse() {
        return AdhesionResponse.builder()
                .numeroReference("ADH-TEST-001")
                .build();
    }

    // -------------------------------------------------------------------------
    // GET /api/adhesions — success
    // -------------------------------------------------------------------------

    @Test
    @Order(1)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAllAdhesions_success_returns200() {
        when(adhesionService.getAllAdhesions(anyInt(), anyInt()))
                .thenReturn(List.of(buildAdhesionResponse()));

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @Order(2)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAllAdhesions_serverError_returns500() {
        when(adhesionService.getAllAdhesions(anyInt(), anyInt()))
                .thenThrow(new RuntimeException("db error"));

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(500);
    }

    // -------------------------------------------------------------------------
    // GET /api/adhesions/{id} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(3)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAdhesionById_success_returns200() {
        when(adhesionService.getAdhesionById(any(UUID.class)))
                .thenReturn(buildAdhesionResponse());

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

    // -------------------------------------------------------------------------
    // GET /api/adhesions/reference/{ref} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(4)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAdhesionByReference_success_returns200() {
        when(adhesionService.getAdhesionByReference(anyString()))
                .thenReturn(buildAdhesionResponse());

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

    // -------------------------------------------------------------------------
    // POST /api/adhesions — success
    // -------------------------------------------------------------------------

    @Test
    @Order(5)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void createAdhesion_success_returns201() {
        when(adhesionService.createAdhesion(any(CreateAdhesionRequest.class)))
                .thenReturn(buildAdhesionResponse());

        String body = String.format("""
                {
                  "numeroReference": "ADH-2026-001",
                  "membreId": "%s",
                  "organisationId": "%s",
                  "dateDemande": "2026-03-22",
                  "fraisAdhesion": 5000,
                  "codeDevise": "XOF"
                }
                """, MEMBRE_ID, ORG_ID);

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

    @Test
    @Order(6)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void createAdhesion_serverError_returns500() {
        when(adhesionService.createAdhesion(any(CreateAdhesionRequest.class)))
                .thenThrow(new RuntimeException("db error"));

        String body = String.format("""
                {"numeroReference": "ADH-ERR-001", "membreId": "%s", "organisationId": "%s", "dateDemande": "2026-03-22", "fraisAdhesion": 5000, "codeDevise": "XOF"}
                """, MEMBRE_ID, ORG_ID);

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

    // -------------------------------------------------------------------------
    // PUT /api/adhesions/{id} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(7)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void updateAdhesion_success_returns200() {
        when(adhesionService.updateAdhesion(any(UUID.class), any(UpdateAdhesionRequest.class)))
                .thenReturn(buildAdhesionResponse());

        String body = """
                {"statut": "EN_ATTENTE"}
                """;

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", ADHESION_ID)
                .body(body)
                .when()
                .put(BASE_PATH + "/{id}")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // DELETE /api/adhesions/{id} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(8)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void deleteAdhesion_success_returns204() {
        doNothing().when(adhesionService).deleteAdhesion(any(UUID.class));

        given()
                .pathParam("id", ADHESION_ID)
                .when()
                .delete(BASE_PATH + "/{id}")
                .then()
                .statusCode(204);
    }

    // -------------------------------------------------------------------------
    // POST /api/adhesions/{id}/approuver — success
    // -------------------------------------------------------------------------

    @Test
    @Order(9)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void approuverAdhesion_success_returns200() {
        when(adhesionService.approuverAdhesion(any(UUID.class), anyString()))
                .thenReturn(buildAdhesionResponse());

        given()
                .pathParam("id", ADHESION_ID)
                .queryParam("approuvePar", "admin@unionflow.com")
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/approuver")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // POST /api/adhesions/{id}/rejeter — success
    // -------------------------------------------------------------------------

    @Test
    @Order(10)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void rejeterAdhesion_success_returns200() {
        when(adhesionService.rejeterAdhesion(any(UUID.class), anyString()))
                .thenReturn(buildAdhesionResponse());

        given()
                .pathParam("id", ADHESION_ID)
                .queryParam("motifRejet", "Dossier incomplet")
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/rejeter")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // POST /api/adhesions/{id}/paiement — success
    // -------------------------------------------------------------------------

    @Test
    @Order(11)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void enregistrerPaiement_success_returns200() {
        when(adhesionService.enregistrerPaiement(any(UUID.class), any(BigDecimal.class), anyString(), anyString()))
                .thenReturn(buildAdhesionResponse());

        given()
                .pathParam("id", ADHESION_ID)
                .queryParam("montantPaye", "5000")
                .queryParam("methodePaiement", "ESPECES")
                .queryParam("referencePaiement", "REF-001")
                .contentType(ContentType.JSON)
                .when()
                .post(BASE_PATH + "/{id}/paiement")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // GET /api/adhesions/membre/{membreId} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(12)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAdhesionsByMembre_success_returns200() {
        when(adhesionService.getAdhesionsByMembre(any(UUID.class), anyInt(), anyInt()))
                .thenReturn(List.of(buildAdhesionResponse()));

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

    // -------------------------------------------------------------------------
    // GET /api/adhesions/organisation/{organisationId} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(13)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAdhesionsByOrganisation_success_returns200() {
        when(adhesionService.getAdhesionsByOrganisation(any(UUID.class), anyInt(), anyInt()))
                .thenReturn(List.of(buildAdhesionResponse()));

        given()
                .pathParam("organisationId", ORG_ID)
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH + "/organisation/{organisationId}")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    // -------------------------------------------------------------------------
    // GET /api/adhesions/statut/{statut} — success
    // -------------------------------------------------------------------------

    @Test
    @Order(14)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAdhesionsByStatut_success_returns200() {
        when(adhesionService.getAdhesionsByStatut(anyString(), anyInt(), anyInt()))
                .thenReturn(List.of(buildAdhesionResponse()));

        given()
                .pathParam("statut", "APPROUVEE")
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH + "/statut/{statut}")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    // -------------------------------------------------------------------------
    // GET /api/adhesions/en-attente — success
    // -------------------------------------------------------------------------

    @Test
    @Order(15)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getAdhesionsEnAttente_success_returns200() {
        when(adhesionService.getAdhesionsEnAttente(anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH + "/en-attente")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // GET /api/adhesions/stats — success
    // -------------------------------------------------------------------------

    @Test
    @Order(16)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getStatistiquesAdhesions_success_returns200() {
        when(adhesionService.getStatistiquesAdhesions())
                .thenReturn(Map.of("total", 100, "enAttente", 10, "approuvees", 85));

        given()
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @Order(17)
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    void getStatistiquesAdhesions_serverError_returns500() {
        when(adhesionService.getStatistiquesAdhesions())
                .thenThrow(new RuntimeException("stats error"));

        given()
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(500);
    }
}
