package dev.lions.unionflow.server.security;

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.OrganisationRepository;
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Optional;
import java.util.UUID;

import static io.restassured.RestAssured.given;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

/**
 * Tests d'intégration pour {@link OrganisationContextFilter}.
 *
 * <p>Vérifie le comportement du filtre multi-org selon le header
 * {@code X-Active-Organisation-Id} :
 * <ul>
 *   <li>Absent → passe (pas de contexte org requis)</li>
 *   <li>UUID invalide → 400 Bad Request</li>
 *   <li>Organisation inexistante → 404 Not Found</li>
 *   <li>Utilisateur non membre de l'org → 403 Forbidden</li>
 *   <li>Membre actif → contexte résolu, requête passée</li>
 *   <li>SUPER_ADMIN → toujours autorisé (bypass membre check)</li>
 * </ul>
 *
 * <p>Utilise l'endpoint {@code GET /api/v1/dashboard/health} (PermitAll-like)
 * comme cible neutre pour tester le filtre.
 */
@QuarkusTest
@DisplayName("OrganisationContextFilter — multi-org header validation")
class OrganisationContextFilterMultiOrgTest {

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    MembreOrganisationRepository membreOrganisationRepository;

    // ─── Aucun header ────────────────────────────────────────────────────────

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("Sans header X-Active-Organisation-Id : filtre passe, endpoint répond")
    void noHeader_filterPasses_dashboardReachable() {
        given()
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(200);
    }

    // ─── UUID invalide ────────────────────────────────────────────────────────

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("Header avec UUID invalide → 400 Bad Request")
    void invalidUuidHeader_returns400() {
        given()
                .header("X-Active-Organisation-Id", "not-a-valid-uuid")
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(400);
    }

    // ─── Organisation introuvable ─────────────────────────────────────────────

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("Header avec UUID valide mais org inexistante → 404 Not Found")
    void validUuidButOrgNotFound_returns404() {
        UUID unknownOrgId = UUID.randomUUID();
        when(organisationRepository.findByIdOptional(unknownOrgId)).thenReturn(Optional.empty());

        given()
                .header("X-Active-Organisation-Id", unknownOrgId.toString())
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(404);
    }

    // ─── Utilisateur non membre ───────────────────────────────────────────────

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("Header valide + utilisateur non membre de l'org → 403 Forbidden")
    void memberNotInOrg_returns403() {
        UUID orgId = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setId(orgId);
        org.setNom("Org Tierce");

        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        membre.setEmail("membre@test.com");

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(membreRepository.findByEmail("membre@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findByMembreIdAndOrganisationId(membre.getId(), orgId))
                .thenReturn(Optional.empty()); // Pas membre

        given()
                .header("X-Active-Organisation-Id", orgId.toString())
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(403);
    }

    // ─── Membre non actif ─────────────────────────────────────────────────────

    @Test
    @TestSecurity(user = "suspendu@test.com", roles = {"MEMBRE"})
    @DisplayName("Header valide + membre suspendu dans l'org → 403 Forbidden")
    void memberSuspended_returns403() {
        UUID orgId = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setId(orgId);
        org.setNom("Org Test");

        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        membre.setEmail("suspendu@test.com");

        MembreOrganisation membreOrg = MembreOrganisation.builder()
                .membre(membre)
                .organisation(org)
                .statutMembre(StatutMembre.SUSPENDU)
                .build();
        membreOrg.setId(UUID.randomUUID());

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(membreRepository.findByEmail("suspendu@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findByMembreIdAndOrganisationId(membre.getId(), orgId))
                .thenReturn(Optional.of(membreOrg));

        given()
                .header("X-Active-Organisation-Id", orgId.toString())
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(403);
    }

    // ─── SUPER_ADMIN bypass ───────────────────────────────────────────────────

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN", "ADMIN"})
    @DisplayName("SUPER_ADMIN avec header valide → bypass check membre, contexte résolu (200)")
    void superAdmin_validOrgId_bypassesMemberCheck() {
        UUID orgId = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setId(orgId);
        org.setNom("Org Admin");

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        // Pas besoin de mocker membreRepository — SUPER_ADMIN bypass

        given()
                .header("X-Active-Organisation-Id", orgId.toString())
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(200);
    }

    // ─── Membre actif ─────────────────────────────────────────────────────────

    @Test
    @TestSecurity(user = "actif@test.com", roles = {"MEMBRE"})
    @DisplayName("Header valide + membre ACTIF dans l'org → contexte résolu (200)")
    void activeMember_validOrgId_contextResolved() {
        UUID orgId = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setId(orgId);
        org.setNom("Ma Tontine");

        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        membre.setEmail("actif@test.com");

        MembreOrganisation membreOrg = MembreOrganisation.builder()
                .membre(membre)
                .organisation(org)
                .statutMembre(StatutMembre.ACTIF)
                .build();
        membreOrg.setId(UUID.randomUUID());

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(membreRepository.findByEmail("actif@test.com")).thenReturn(Optional.of(membre));
        when(membreOrganisationRepository.findByMembreIdAndOrganisationId(membre.getId(), orgId))
                .thenReturn(Optional.of(membreOrg));

        given()
                .header("X-Active-Organisation-Id", orgId.toString())
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(200);
    }
}
