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

import dev.lions.unionflow.server.api.dto.membre.CompteAdherentResponse;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.MembreOrganisation;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.MembreOrganisationRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.SouscriptionOrganisationRepository;
import dev.lions.unionflow.server.service.CompteAdherentService;
import dev.lions.unionflow.server.service.MembreKeycloakSyncService;
import dev.lions.unionflow.server.service.MembreService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Optional;
import java.util.UUID;

@QuarkusTest
@DisplayName("CompteAdherentResource")
class CompteAdherentResourceTest {

    @InjectMock
    CompteAdherentService compteAdherentService;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    MembreOrganisationRepository membreOrganisationRepository;

    @InjectMock
    SouscriptionOrganisationRepository souscriptionOrganisationRepository;

    @InjectMock
    MembreService membreService;

    @InjectMock
    MembreKeycloakSyncService membreKeycloakSyncService;

    // ============================================================
    // getMonCompte
    // ============================================================

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-compte retourne 200 avec le compte adhérent")
    void getMonCompte_returns200() {
        CompteAdherentResponse response = new CompteAdherentResponse(
                "MUF-2026-001",
                "Jean Dupont",
                null,
                null,
                "ACTIF",
                new BigDecimal("215000"),
                new BigDecimal("100000"),
                new BigDecimal("0"),
                new BigDecimal("315000"),
                new BigDecimal("0"),
                new BigDecimal("300000"),
                5,
                6,
                1,
                83,
                2,
                null
        );
        when(compteAdherentService.getMonCompte()).thenReturn(response);

        given()
                .when()
                .get("/api/membres/mon-compte")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body(notNullValue());
    }

    // ============================================================
    // getMonStatut
    // ============================================================

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("GET /api/membres/mon-statut → ACTIF si aucun fiche membre (admin pur)")
    void getMonStatut_noMembre_returnsActif() {
        when(membreRepository.findByEmail(any())).thenReturn(Optional.empty());

        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(200)
                .body("statutCompte", equalTo("ACTIF"));
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-statut → ACTIF si membre déjà ACTIF en base")
    void getMonStatut_membreActif_returnsActif() {
        Membre membre = membreFixture("membre@test.com", "ACTIF");
        when(membreRepository.findByEmail("membre@test.com")).thenReturn(Optional.of(membre));

        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(200)
                .body("statutCompte", equalTo("ACTIF"));

        verify(membreService, never()).activerMembre(any());
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-statut → auto-active et retourne ACTIF si org a souscription active")
    void getMonStatut_enAttenteAvecSouscriptionActive_autoActiveEtRetourneActif() {
        UUID membreId = UUID.randomUUID();
        UUID orgId = UUID.randomUUID();

        Membre membre = membreFixture("membre@test.com", "EN_ATTENTE_VALIDATION");
        membre.setId(membreId);

        Organisation org = new Organisation();
        org.setId(orgId);

        MembreOrganisation mo = new MembreOrganisation();
        mo.setOrganisation(org);

        when(membreRepository.findByEmail("membre@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findFirstByMembreId(membreId)).thenReturn(Optional.of(mo));
        when(membreService.orgHasActiveSubscription(orgId)).thenReturn(true);
        when(membreService.activerMembre(membreId)).thenReturn(membre);
        doNothing().when(membreKeycloakSyncService).activerMembreDansKeycloak(membreId);

        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(200)
                .body("statutCompte", equalTo("ACTIF"));

        verify(membreService).activerMembre(membreId);
        verify(membreKeycloakSyncService).activerMembreDansKeycloak(membreId);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-statut → auto-activation Keycloak échouée reste ACTIF (non bloquant)")
    void getMonStatut_enAttenteAvecSouscriptionActive_keycloakFail_retourneActif() {
        UUID membreId = UUID.randomUUID();
        UUID orgId = UUID.randomUUID();

        Membre membre = membreFixture("membre@test.com", "EN_ATTENTE_VALIDATION");
        membre.setId(membreId);

        Organisation org = new Organisation();
        org.setId(orgId);

        MembreOrganisation mo = new MembreOrganisation();
        mo.setOrganisation(org);

        when(membreRepository.findByEmail("membre@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findFirstByMembreId(membreId)).thenReturn(Optional.of(mo));
        when(membreService.orgHasActiveSubscription(orgId)).thenReturn(true);
        when(membreService.activerMembre(membreId)).thenReturn(membre);
        doThrow(new RuntimeException("Keycloak down"))
                .when(membreKeycloakSyncService).activerMembreDansKeycloak(membreId);

        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(200)
                .body("statutCompte", equalTo("ACTIF"));
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-statut → EN_ATTENTE_VALIDATION + NO_SUBSCRIPTION si org sans souscription")
    void getMonStatut_enAttenteSansSouscription_retourneEnAttenteNoSubscription() {
        UUID membreId = UUID.randomUUID();
        UUID orgId = UUID.randomUUID();

        Membre membre = membreFixture("membre@test.com", "EN_ATTENTE_VALIDATION");
        membre.setId(membreId);

        Organisation org = new Organisation();
        org.setId(orgId);
        org.setTypeOrganisation("ASSOCIATION");

        MembreOrganisation mo = new MembreOrganisation();
        mo.setOrganisation(org);

        when(membreRepository.findByEmail("membre@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findFirstByMembreId(membreId)).thenReturn(Optional.of(mo));
        when(membreService.orgHasActiveSubscription(orgId)).thenReturn(false);
        when(souscriptionOrganisationRepository.findLatestByOrganisationId(orgId))
                .thenReturn(Optional.empty());

        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(200)
                .body("statutCompte", equalTo("EN_ATTENTE_VALIDATION"))
                .body("onboardingState", equalTo("NO_SUBSCRIPTION"));

        verify(membreService, never()).activerMembre(any());
    }

    // ============================================================
    // Error cases
    // ============================================================

    @Test
    @DisplayName("GET /api/membres/mon-compte sans authentification retourne 401")
    void getMonCompte_sansAuthentification_returns401() {
        given()
                .when()
                .get("/api/membres/mon-compte")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-compte retourne 500 quand le service lève une exception")
    void getMonCompte_serviceException_returns500() {
        when(compteAdherentService.getMonCompte())
                .thenThrow(new RuntimeException("Erreur base de données"));

        given()
                .when()
                .get("/api/membres/mon-compte")
                .then()
                .statusCode(500);
    }

    @Test
    @DisplayName("GET /api/membres/mon-statut sans authentification retourne 401")
    void getMonStatut_sansAuthentification_returns401() {
        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/membres/mon-statut retourne EN_ATTENTE_VALIDATION quand aucune org trouvée")
    void getMonStatut_enAttenteMembreOrganisationAbsente_retourneEnAttente() {
        UUID membreId = UUID.randomUUID();

        Membre membre = membreFixture("membre@test.com", "EN_ATTENTE_VALIDATION");
        membre.setId(membreId);

        when(membreRepository.findByEmail("membre@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findFirstByMembreId(membreId)).thenReturn(Optional.empty());

        given()
                .when()
                .get("/api/membres/mon-statut")
                .then()
                .statusCode(200)
                .body("statutCompte", equalTo("EN_ATTENTE_VALIDATION"));
    }

    // ─── Helper ────────────────────────────────────────────────────────────────

    private Membre membreFixture(String email, String statut) {
        Membre m = new Membre();
        m.setId(UUID.randomUUID());
        m.setEmail(email);
        m.setNom("Test");
        m.setPrenom("User");
        m.setDateNaissance(LocalDate.of(1990, 1, 1));
        m.setNumeroMembre("UF-0001");
        m.setStatutCompte(statut);
        m.setActif("ACTIF".equals(statut));
        return m;
    }
}
