package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hamcrest.Matchers.*;

import dev.lions.unionflow.server.api.dto.solidarite.request.UpdateDemandeAideRequest;
import dev.lions.unionflow.server.entity.DemandeAide;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.DemandeAideRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class DemandeAideResourceTest {

    private static final String TEST_MEMBRE_EMAIL = "membre-demande-resource-test@unionflow.dev";

    @Inject
    MembreRepository membreRepository;

    @Inject
    OrganisationRepository organisationRepository;

    @Inject
    DemandeAideRepository demandeAideRepository;

    @Inject
    DemandeAideResource demandeAideResource;

    private Membre testMembre;
    private Organisation testOrganisation;

    @BeforeEach
    @Transactional
    void setupTestData() {
        // Create test member if not exists
        testMembre = membreRepository.findByEmail(TEST_MEMBRE_EMAIL).orElse(null);
        if (testMembre == null) {
            testMembre = new Membre();
            testMembre.setNom("DemandeAide");
            testMembre.setPrenom("ResourceTest");
            testMembre.setEmail(TEST_MEMBRE_EMAIL);
            testMembre.setNumeroMembre("M-DAIDE-" + UUID.randomUUID().toString().substring(0, 8));
            testMembre.setDateNaissance(LocalDate.of(1990, 1, 1));
            testMembre.setActif(true);
            testMembre.setDateCreation(LocalDateTime.now());
            membreRepository.persist(testMembre);
        }
        // Create test organisation
        testOrganisation = new Organisation();
        testOrganisation.setNom("Org DemandeAide Test " + UUID.randomUUID().toString().substring(0, 8));
        testOrganisation.setTypeOrganisation("ASSOCIATION");
        testOrganisation.setStatut("ACTIVE");
        testOrganisation.setEmail("org-daide-" + UUID.randomUUID().toString().substring(0, 8) + "@test.com");
        testOrganisation.setActif(true);
        organisationRepository.persist(testOrganisation);
    }

    @AfterEach
    @Transactional
    void cleanupTestData() {
        // Delete demandes liées au testMembre avant de supprimer le membre (contrainte FK)
        if (testMembre != null) {
            List<DemandeAide> demandes = demandeAideRepository.findByDemandeurId(testMembre.getId());
            demandes.forEach(d -> demandeAideRepository.delete(d));
        }
        // Supprimer organisation test
        if (testOrganisation != null) {
            Organisation o = organisationRepository.findByIdOptional(testOrganisation.getId()).orElse(null);
            if (o != null) organisationRepository.delete(o);
        }
        // Supprimer membre test
        if (testMembre != null) {
            Membre m = membreRepository.findByIdOptional(testMembre.getId()).orElse(null);
            if (m != null) membreRepository.delete(m);
        }
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide retourne 200")
    void listerToutes_returns200() {
        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get("/api/demandes-aide")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = TEST_MEMBRE_EMAIL, roles = { "MEMBRE" })
    @DisplayName("GET /api/demandes-aide/mes avec membre existant et 0 demandes → from < to false → liste vide (L61 false)")
    void mesDemandes_membreExistantSansDemandes_fromLtToFalse_returnsEmpty() {
        // Membre existe en DB (créé dans @BeforeEach), mais n'a aucune demande
        // → rechercherAvecFiltres returns [] → all.size()=0 → from=0, to=0 → from < to = false → List.of()
        given()
                .queryParam("page", 0)
                .queryParam("size", 10)
                .when()
                .get("/api/demandes-aide/mes")
                .then()
                .statusCode(200)
                .body("$", hasSize(0));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/{id} inexistant retourne 404")
    void obtenirParId_inexistant_returns404() {
        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .get("/api/demandes-aide/{id}")
                .then()
                .statusCode(404);
    }

    // --- rechercher() missed branches ---

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search sans params retourne 200")
    void rechercher_noParams_returns200() {
        given()
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec statut valide couvre branche StatutAide.valueOf")
    void rechercher_validStatut_returns200() {
        given()
                .queryParam("statut", "EN_ATTENTE")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec statut invalide couvre catch IllegalArgumentException (statut)")
    void rechercher_invalidStatut_returns200() {
        given()
                .queryParam("statut", "STATUT_INCONNU")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec type valide couvre branche TypeAide.valueOf")
    void rechercher_validType_returns200() {
        given()
                .queryParam("type", "AIDE_FINANCIERE_URGENTE")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec type invalide couvre catch IllegalArgumentException (type)")
    void rechercher_invalidType_returns200() {
        given()
                .queryParam("type", "TYPE_INCONNU")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec urgence valide couvre branche PrioriteAide.valueOf")
    void rechercher_validUrgence_returns200() {
        given()
                .queryParam("urgence", "URGENTE")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec urgence invalide couvre catch IllegalArgumentException (urgence)")
    void rechercher_invalidUrgence_returns200() {
        given()
                .queryParam("urgence", "URGENCE_INCONNUE")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    // ── Branches manquantes ───────────────────────────────────────────────────

    @Test
    @TestSecurity(user = "inconnu@unionflow.com", roles = { "MEMBRE" })
    @DisplayName("GET /api/demandes-aide/mes avec membre non trouvé → retourne 200 et liste vide (branche membre null)")
    void mesDemandes_membreNonTrouve_retourneListeVide() {
        // Utilisateur "inconnu@unionflow.com" n'existe pas → membre null → return List.of()
        given()
                .when()
                .get("/api/demandes-aide/mes")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "MEMBRE" })
    @DisplayName("GET /api/demandes-aide/mes avec membre existant → branche membre non-null couverte")
    void mesDemandes_membreExistant_returns200() {
        // admin@unionflow.com peut exister ou non, mais la branche est couverte
        given()
                .queryParam("page", 0)
                .queryParam("size", 10)
                .when()
                .get("/api/demandes-aide/mes")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide avec page grande → from >= size → retourne liste vide (branche from < to false)")
    void listerToutes_pageTropGrande_retourneListeVide() {
        given()
                .queryParam("page", 999999)
                .queryParam("size", 1)
                .when()
                .get("/api/demandes-aide")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec statut empty → branche statut.isEmpty() true")
    void rechercher_emptyStatut_returns200() {
        given()
                .queryParam("statut", "")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec type empty → branche type.isEmpty() true")
    void rechercher_emptyType_returns200() {
        given()
                .queryParam("type", "")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec urgence empty → branche urgence.isEmpty() true")
    void rechercher_emptyUrgence_returns200() {
        given()
                .queryParam("urgence", "")
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("GET /api/demandes-aide/search avec page grande → branche from < to false (liste vide)")
    void rechercher_pageTropGrande_retourneListeVide() {
        given()
                .queryParam("page", 999999)
                .queryParam("size", 1)
                .when()
                .get("/api/demandes-aide/search")
                .then()
                .statusCode(200);
    }

    // ── creer, mettreAJour, approuver, rejeter ───────────────────────────────

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("POST /api/demandes-aide → creer une demande valide → 201")
    void creer_demandeValide_returns201() {
        given()
                .contentType(ContentType.JSON)
                .body(Map.of(
                        "typeAide", "AIDE_COTISATION",
                        "titre", "Demande test coverage creer",
                        "description", "Description test pour la couverture du endpoint creer",
                        "membreDemandeurId", testMembre.getId().toString(),
                        "associationId", testOrganisation.getId().toString()
                ))
                .when()
                .post("/api/demandes-aide")
                .then()
                .statusCode(201);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("PUT /api/demandes-aide/{id} → mettreAJour (id inexistant → 404/500 mais méthode couverte)")
    void mettreAJour_idInexistant_retourne4xxOu5xx() {
        given()
                .contentType(ContentType.JSON)
                .body(Map.of("titre", "Titre mis à jour"))
                .pathParam("id", UUID.randomUUID())
                .when()
                .put("/api/demandes-aide/{id}")
                .then()
                .statusCode(anyOf(equalTo(404), equalTo(400), equalTo(500)));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("PUT /api/demandes-aide/{id}/approuver → approuver (id inexistant → méthode couverte)")
    void approuver_idInexistant_retourne4xxOu5xx() {
        given()
                .queryParam("motif", "Test motif approbation")
                .pathParam("id", UUID.randomUUID())
                .when()
                .put("/api/demandes-aide/{id}/approuver")
                .then()
                .statusCode(anyOf(equalTo(404), equalTo(400), equalTo(500)));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN" })
    @DisplayName("PUT /api/demandes-aide/{id}/rejeter → rejeter (id inexistant → méthode couverte)")
    void rejeter_idInexistant_retourne4xxOu5xx() {
        given()
                .queryParam("motif", "Test motif rejet")
                .pathParam("id", UUID.randomUUID())
                .when()
                .put("/api/demandes-aide/{id}/rejeter")
                .then()
                .statusCode(anyOf(equalTo(404), equalTo(400), equalTo(500)));
    }

    // ── Tests arc_contextualInstance (bypass proxy+interceptors) ─────────────

    private DemandeAideResource realResource() {
        return (DemandeAideResource) ((io.quarkus.arc.ClientProxy) demandeAideResource).arc_contextualInstance();
    }

    @Test
    @TestTransaction
    @DisplayName("DemandeAideResource.approuver — arc_contextualInstance bypass L131")
    void approuver_arcDirect_couvreMethode() {
        assertThatThrownBy(() -> realResource().approuver(UUID.randomUUID(), "motif"))
                .isInstanceOf(Exception.class);
    }

    @Test
    @TestTransaction
    @DisplayName("DemandeAideResource.rejeter — arc_contextualInstance bypass L138")
    void rejeter_arcDirect_couvreMethode() {
        assertThatThrownBy(() -> realResource().rejeter(UUID.randomUUID(), "motif"))
                .isInstanceOf(Exception.class);
    }

    @Test
    @TestTransaction
    @DisplayName("DemandeAideResource.mettreAJour — arc_contextualInstance bypass L124")
    void mettreAJour_arcDirect_couvreMethode() {
        assertThatThrownBy(() -> realResource().mettreAJour(
                UUID.randomUUID(),
                UpdateDemandeAideRequest.builder().titre("Test").build()))
                .isInstanceOf(Exception.class);
    }
}
