package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
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.service.MemberLifecycleService;
import dev.lions.unionflow.server.service.MembreKeycloakSyncService;
import dev.lions.unionflow.server.service.MembreService;
import dev.lions.unionflow.server.service.MembreSuiviService;
import dev.lions.unionflow.server.service.OrganisationService;
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.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

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

/**
 * Tests RBAC pour les endpoints lifecycle du cycle de vie des membres.
 *
 * <p>Vérifie que :
 * <ul>
 *   <li>MEMBRE ne peut pas activer/suspendre/radier (403)</li>
 *   <li>ADMIN_ORGANISATION peut activer/suspendre/radier (200)</li>
 *   <li>SUPER_ADMIN peut accéder à tous les endpoints admin (200)</li>
 *   <li>accepter-invitation est PermitAll (accessible sans auth)</li>
 * </ul>
 */
@QuarkusTest
@DisplayName("MembreResource — RBAC lifecycle endpoints")
class MembreResourceLifecycleRbacTest {

    @InjectMock
    MembreService membreService;

    @InjectMock
    MembreKeycloakSyncService keycloakSyncService;

    @InjectMock
    MembreSuiviService membreSuiviService;

    @InjectMock
    OrganisationService organisationService;

    @InjectMock
    MemberLifecycleService memberLifecycleService;

    @InjectMock
    MembreOrganisationRepository membreOrgRepository;

    @InjectMock
    org.eclipse.microprofile.jwt.JsonWebToken jwt;

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

    private MembreOrganisation buildLien(UUID id, StatutMembre statut) {
        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        membre.setNom("Test");
        membre.setPrenom("User");

        Organisation org = new Organisation();
        org.setId(UUID.randomUUID());
        org.setNom("Org Test");

        MembreOrganisation lien = MembreOrganisation.builder()
                .membre(membre)
                .organisation(org)
                .statutMembre(statut)
                .build();
        lien.setId(id);
        return lien;
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  activer-adhesion (PUT /{membreOrgId}/activer-adhesion)
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT activer-adhesion: MEMBRE → 403 Forbidden")
    void activerAdhesion_membreRole_returns403() {
        given()
                .contentType(ContentType.JSON)
                .body(Map.of("motif", "test"))
                .when()
                .put("/api/membres/" + UUID.randomUUID() + "/activer-adhesion")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("PUT activer-adhesion: ADMIN_ORGANISATION → 200")
    void activerAdhesion_adminOrganisation_returns200() {
        UUID membreOrgId = UUID.randomUUID();
        var lien = buildLien(membreOrgId, StatutMembre.EN_ATTENTE_VALIDATION);
        lien.setStatutMembre(StatutMembre.ACTIF); // Résultat après activation

        when(memberLifecycleService.activerMembre(any(UUID.class), any(), any()))
                .thenReturn(lien);
        when(jwt.getSubject()).thenReturn(UUID.randomUUID().toString());

        given()
                .contentType(ContentType.JSON)
                .body(Map.of("motif", "validation"))
                .when()
                .put("/api/membres/" + membreOrgId + "/activer-adhesion")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    @DisplayName("PUT activer-adhesion: SUPER_ADMIN → 200")
    void activerAdhesion_superAdmin_returns200() {
        UUID membreOrgId = UUID.randomUUID();
        var lien = buildLien(membreOrgId, StatutMembre.ACTIF);

        when(memberLifecycleService.activerMembre(any(UUID.class), any(), any()))
                .thenReturn(lien);
        when(jwt.getSubject()).thenReturn(UUID.randomUUID().toString());

        given()
                .contentType(ContentType.JSON)
                .body(Map.of())
                .when()
                .put("/api/membres/" + membreOrgId + "/activer-adhesion")
                .then()
                .statusCode(200);
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  suspendre-adhesion (PUT /{membreOrgId}/suspendre-adhesion)
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT suspendre-adhesion: MEMBRE → 403 Forbidden")
    void suspendrAdhesion_membreRole_returns403() {
        given()
                .contentType(ContentType.JSON)
                .body(Map.of("motif", "test"))
                .when()
                .put("/api/membres/" + UUID.randomUUID() + "/suspendre-adhesion")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("PUT suspendre-adhesion: ADMIN_ORGANISATION → 200")
    void suspendrAdhesion_adminOrganisation_returns200() {
        UUID membreOrgId = UUID.randomUUID();
        var lien = buildLien(membreOrgId, StatutMembre.SUSPENDU);

        when(memberLifecycleService.suspendreMembre(any(UUID.class), any(), any()))
                .thenReturn(lien);
        when(jwt.getSubject()).thenReturn(UUID.randomUUID().toString());

        given()
                .contentType(ContentType.JSON)
                .body(Map.of("motif", "manquements répétés"))
                .when()
                .put("/api/membres/" + membreOrgId + "/suspendre-adhesion")
                .then()
                .statusCode(200);
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  radier-adhesion (PUT /{membreOrgId}/radier-adhesion)
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT radier-adhesion: MEMBRE → 403 Forbidden")
    void radierAdhesion_membreRole_returns403() {
        given()
                .contentType(ContentType.JSON)
                .body(Map.of())
                .when()
                .put("/api/membres/" + UUID.randomUUID() + "/radier-adhesion")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("PUT radier-adhesion: ADMIN → 200")
    void radierAdhesion_admin_returns200() {
        UUID membreOrgId = UUID.randomUUID();
        var lien = buildLien(membreOrgId, StatutMembre.RADIE);

        when(memberLifecycleService.radierMembre(any(UUID.class), any(), any()))
                .thenReturn(lien);
        when(jwt.getSubject()).thenReturn(UUID.randomUUID().toString());

        given()
                .contentType(ContentType.JSON)
                .body(Map.of("motif", "exclusion définitive"))
                .when()
                .put("/api/membres/" + membreOrgId + "/radier-adhesion")
                .then()
                .statusCode(200);
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  inviter-organisation (PUT /{membreId}/inviter-organisation)
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @TestSecurity(user = "user@test.com", roles = {"USER"})
    @DisplayName("PUT inviter-organisation: USER → 403 Forbidden")
    void inviterOrganisation_userRole_returns403() {
        given()
                .contentType(ContentType.JSON)
                .queryParam("organisationId", UUID.randomUUID().toString())
                .when()
                .put("/api/membres/" + UUID.randomUUID() + "/inviter-organisation")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("PUT inviter-organisation: ADMIN_ORGANISATION avec membre inexistant → 404")
    void inviterOrganisation_adminOrganisation_membreNotFound_returns404() {
        UUID membreId = UUID.randomUUID();
        UUID orgId = UUID.randomUUID();

        when(membreService.trouverParId(membreId)).thenReturn(Optional.empty());

        given()
                .contentType(ContentType.JSON)
                .queryParam("organisationId", orgId.toString())
                .when()
                .put("/api/membres/" + membreId + "/inviter-organisation")
                .then()
                .statusCode(404);
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  accepter-invitation (POST /accepter-invitation/{token}) — PermitAll
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("POST accepter-invitation: sans authentification → PermitAll (token invalide → 400 ou 404)")
    void accepterInvitation_noAuth_permitAll() {
        // Pas d'auth — le endpoint est PermitAll
        when(memberLifecycleService.accepterInvitation(anyString()))
                .thenThrow(new IllegalArgumentException("Invitation introuvable ou déjà utilisée."));

        given()
                .contentType(ContentType.JSON)
                .when()
                .post("/api/membres/accepter-invitation/invalidtoken123")
                .then()
                .statusCode(400); // BadRequest pour IllegalArgumentException
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("POST accepter-invitation: MEMBRE avec token valide → 200")
    void accepterInvitation_membre_validToken_returns200() {
        String token = "validtoken1234567890abcdefghi12";
        var lien = buildLien(UUID.randomUUID(), StatutMembre.EN_ATTENTE_VALIDATION);

        when(memberLifecycleService.accepterInvitation(token)).thenReturn(lien);

        given()
                .contentType(ContentType.JSON)
                .when()
                .post("/api/membres/accepter-invitation/" + token)
                .then()
                .statusCode(200)
                .body("statut", notNullValue());
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  adhesion statut (GET /{membreId}/adhesion) — lecture multi-rôle
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /{membreId}/adhesion: MEMBRE peut consulter son statut (200 ou 404)")
    void getAdhesionStatut_membreRole_allowedToRead() {
        UUID membreId = UUID.randomUUID();
        UUID orgId = UUID.randomUUID();

        when(membreOrgRepository.findByMembreIdAndOrganisationId(any(), any()))
                .thenReturn(Optional.empty());

        given()
                .queryParam("organisationId", orgId.toString())
                .when()
                .get("/api/membres/" + membreId + "/adhesion")
                .then()
                .statusCode(404); // Not found, mais pas 403
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /{membreId}/adhesion: ADMIN_ORGANISATION peut consulter (200)")
    void getAdhesionStatut_adminOrganisation_returns200() {
        UUID membreId = UUID.randomUUID();
        UUID orgId = UUID.randomUUID();
        var lien = buildLien(UUID.randomUUID(), StatutMembre.ACTIF);

        when(membreOrgRepository.findByMembreIdAndOrganisationId(any(), any()))
                .thenReturn(Optional.of(lien));

        given()
                .queryParam("organisationId", orgId.toString())
                .when()
                .get("/api/membres/" + membreId + "/adhesion")
                .then()
                .statusCode(200)
                .body("statut", notNullValue());
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  archiver-adhesion (PUT /{membreOrgId}/archiver-adhesion)
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("PUT archiver-adhesion: MEMBRE → 403 Forbidden")
    void archiverAdhesion_membreRole_returns403() {
        given()
                .contentType(ContentType.JSON)
                .body(Map.of())
                .when()
                .put("/api/membres/" + UUID.randomUUID() + "/archiver-adhesion")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("PUT archiver-adhesion: ADMIN_ORGANISATION → 200")
    void archiverAdhesion_adminOrganisation_returns200() {
        UUID membreOrgId = UUID.randomUUID();
        var lien = buildLien(membreOrgId, StatutMembre.ARCHIVE);

        when(memberLifecycleService.archiverMembre(any(UUID.class), any()))
                .thenReturn(lien);

        given()
                .contentType(ContentType.JSON)
                .body(Map.of("motif", "archivage annuel"))
                .when()
                .put("/api/membres/" + membreOrgId + "/archiver-adhesion")
                .then()
                .statusCode(200);
    }
}
