package dev.lions.unionflow.server.integration;

import dev.lions.unionflow.server.entity.*;
import dev.lions.unionflow.server.repository.*;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.*;

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

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Tests d'isolation cross-tenant via PostgreSQL RLS (Row-Level Security).
 *
 * <p>Vérifie qu'un membre d'une organisation ne peut pas accéder aux données d'une autre
 * organisation même avec un accès direct au repository (contournement intentionnel pour test).
 *
 * <p>NOTE : ces tests utilisent PostgreSQL réel (DevServices).
 * Ils ne passent PAS avec H2 (RLS non supporté par H2).
 * Lancer avec : {@code mvn test -Dquarkus.test.profile=integration-test}
 */
@QuarkusTest
@TestProfile(IntegrationTestProfile.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class RlsCrossTenantIsolationTest {

    @Inject
    OrganisationRepository organisationRepository;

    @Inject
    CotisationRepository cotisationRepository;

    @Inject
    MembreRepository membreRepository;

    @Inject
    EntityManager em;

    private static UUID orgAId;
    private static UUID orgBId;

    @BeforeEach
    @Transactional
    void setup() {
        // Créer deux organisations de test
        Organisation orgA = new Organisation();
        orgA.setNom("Org Test A RLS");
        orgA.setActif(true);
        organisationRepository.persist(orgA);
        orgAId = orgA.getId();

        Organisation orgB = new Organisation();
        orgB.setNom("Org Test B RLS");
        orgB.setActif(true);
        organisationRepository.persist(orgB);
        orgBId = orgB.getId();

        // Créer un membre pour orgA
        Membre membreA = new Membre();
        membreA.setEmail("membre-a@test.rls");
        membreA.setPrenom("Membre");
        membreA.setNom("A");
        membreA.setActif(true);
        membreRepository.persist(membreA);

        // Créer une cotisation pour orgA
        Cotisation cotisationA = new Cotisation();
        cotisationA.setOrganisation(orgA);
        cotisationA.setMembre(membreA);
        cotisationA.setMontantDu(BigDecimal.valueOf(5000));
        cotisationA.setMontantPaye(BigDecimal.ZERO);
        cotisationA.setStatut("EN_ATTENTE");
        cotisationA.setDateEcheance(LocalDate.now().plusDays(30));
        cotisationA.setPeriode("2026/04");
        cotisationRepository.persist(cotisationA);
    }

    @Test
    @Order(1)
    @Transactional
    void sansSuperAdmin_cotisationOrgA_visibleUniquementPourOrgA() {
        // SET LOCAL en SQL direct pour simuler le comportement RLS du filtre JAX-RS
        em.createNativeQuery("SET LOCAL app.current_org_id = '" + orgAId + "'").executeUpdate();
        em.createNativeQuery("SET LOCAL app.is_super_admin = 'false'").executeUpdate();

        List<Cotisation> cotisationsVues = cotisationRepository.find("organisation.id", orgAId).list();
        assertThat(cotisationsVues).isNotEmpty();
    }

    @Test
    @Order(2)
    @Transactional
    void sansSuperAdmin_cotisationOrgA_invisibleDepuisOrgB() {
        // Simuler le contexte de orgB — ne devrait pas voir les cotisations de orgA
        em.createNativeQuery("SET LOCAL app.current_org_id = '" + orgBId + "'").executeUpdate();
        em.createNativeQuery("SET LOCAL app.is_super_admin = 'false'").executeUpdate();

        List<Cotisation> cotisationsVues = cotisationRepository.find("organisation.id", orgAId).list();
        // Avec RLS actif : zéro résultat car orgB n'a pas accès aux données de orgA
        assertThat(cotisationsVues).isEmpty();
    }

    @Test
    @Order(3)
    @Transactional
    void avecSuperAdmin_cotisationOrgA_visibleDepuisOrgB() {
        // SUPER_ADMIN contourne la politique RLS
        em.createNativeQuery("SET LOCAL app.current_org_id = '" + orgBId + "'").executeUpdate();
        em.createNativeQuery("SET LOCAL app.is_super_admin = 'true'").executeUpdate();

        List<Cotisation> cotisationsVues = cotisationRepository.find("organisation.id", orgAId).list();
        assertThat(cotisationsVues).isNotEmpty();
    }
}
