package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.InscriptionEvenement;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.TestTransaction;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

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

/**
 * Tests pour {@link InscriptionEvenementRepository} — couvre les 6 méthodes
 * non testées (findByMembreAndEvenement, findByMembre, findByEvenement,
 * findConfirmeesByEvenement, countConfirmeesByEvenement, isMembreInscrit, softDelete).
 */
@QuarkusTest
class InscriptionEvenementRepositoryTest {

    @Inject
    InscriptionEvenementRepository inscriptionRepository;

    @Inject
    MembreRepository membreRepository;

    @Inject
    EvenementRepository evenementRepository;

    @Inject
    OrganisationRepository organisationRepository;

    // ── Helpers ──────────────────────────────────────────────────────────────

    private Membre persistMembre() {
        Membre m = new Membre();
        m.setNumeroMembre("INSC-" + UUID.randomUUID().toString().substring(0, 8));
        m.setPrenom("Inscrit");
        m.setNom("Test");
        m.setEmail("insc-" + UUID.randomUUID() + "@test.com");
        m.setDateNaissance(LocalDate.of(1990, 1, 1));
        m.setActif(true);
        membreRepository.persist(m);
        return m;
    }

    private Organisation persistOrganisation() {
        Organisation o = new Organisation();
        o.setNom("Org Insc " + UUID.randomUUID().toString().substring(0, 8));
        o.setTypeOrganisation("ASSOCIATION");
        o.setStatut("ACTIVE");
        o.setEmail("org-insc-" + UUID.randomUUID() + "@test.com");
        o.setActif(true);
        organisationRepository.persist(o);
        return o;
    }

    private Evenement persistEvenement(Organisation org) {
        Evenement e = new Evenement();
        e.setTitre("Evenement Test " + UUID.randomUUID().toString().substring(0, 8));
        e.setDateDebut(LocalDateTime.now().plusDays(7));
        e.setStatut("PLANIFIE");
        e.setActif(true);
        evenementRepository.persist(e);
        return e;
    }

    private InscriptionEvenement persistInscription(Membre membre, Evenement evenement, String statut) {
        InscriptionEvenement ie = new InscriptionEvenement();
        ie.setMembre(membre);
        ie.setEvenement(evenement);
        ie.setStatut(statut);
        ie.setDateInscription(LocalDateTime.now());
        ie.setActif(true);
        inscriptionRepository.persist(ie);
        return ie;
    }

    // ── Tests ─────────────────────────────────────────────────────────────────

    @Test
    @TestTransaction
    @DisplayName("findByMembreAndEvenement retourne l'inscription existante")
    void findByMembreAndEvenement_returnsInscription() {
        Membre m = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e = persistEvenement(o);
        persistInscription(m, e, "CONFIRMEE");

        Optional<InscriptionEvenement> found = inscriptionRepository
                .findByMembreAndEvenement(m.getId(), e.getId());
        assertThat(found).isPresent();
        assertThat(found.get().getMembre().getId()).isEqualTo(m.getId());
    }

    @Test
    @TestTransaction
    @DisplayName("findByMembreAndEvenement retourne empty si aucune inscription")
    void findByMembreAndEvenement_noInscription_returnsEmpty() {
        Optional<InscriptionEvenement> found = inscriptionRepository
                .findByMembreAndEvenement(UUID.randomUUID(), UUID.randomUUID());
        assertThat(found).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByMembre retourne les inscriptions du membre")
    void findByMembre_returnsList() {
        Membre m = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e1 = persistEvenement(o);
        Evenement e2 = persistEvenement(o);
        persistInscription(m, e1, "CONFIRMEE");
        persistInscription(m, e2, "EN_ATTENTE");

        List<InscriptionEvenement> list = inscriptionRepository.findByMembre(m.getId());
        assertThat(list).hasSizeGreaterThanOrEqualTo(2);
    }

    @Test
    @TestTransaction
    @DisplayName("findByMembre retourne liste vide pour membre sans inscription")
    void findByMembre_noInscriptions_returnsEmpty() {
        List<InscriptionEvenement> list = inscriptionRepository.findByMembre(UUID.randomUUID());
        assertThat(list).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByEvenement retourne les inscriptions de l'événement")
    void findByEvenement_returnsList() {
        Membre m1 = persistMembre();
        Membre m2 = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e = persistEvenement(o);
        persistInscription(m1, e, "CONFIRMEE");
        persistInscription(m2, e, "EN_ATTENTE");

        List<InscriptionEvenement> list = inscriptionRepository.findByEvenement(e.getId());
        assertThat(list).hasSizeGreaterThanOrEqualTo(2);
    }

    @Test
    @TestTransaction
    @DisplayName("findConfirmeesByEvenement retourne uniquement les inscriptions CONFIRMEE")
    void findConfirmeesByEvenement_onlyConfirmees() {
        Membre m1 = persistMembre();
        Membre m2 = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e = persistEvenement(o);
        persistInscription(m1, e, "CONFIRMEE");
        persistInscription(m2, e, "EN_ATTENTE");

        List<InscriptionEvenement> confirmees = inscriptionRepository.findConfirmeesByEvenement(e.getId());
        assertThat(confirmees).isNotEmpty();
        assertThat(confirmees).allMatch(ie -> "CONFIRMEE".equals(ie.getStatut()));
    }

    @Test
    @TestTransaction
    @DisplayName("countConfirmeesByEvenement retourne le bon compte")
    void countConfirmeesByEvenement_returnsCount() {
        Membre m1 = persistMembre();
        Membre m2 = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e = persistEvenement(o);
        persistInscription(m1, e, "CONFIRMEE");
        persistInscription(m2, e, "EN_ATTENTE");

        long count = inscriptionRepository.countConfirmeesByEvenement(e.getId());
        assertThat(count).isEqualTo(1L);
    }

    @Test
    @TestTransaction
    @DisplayName("isMembreInscrit retourne true si inscription CONFIRMEE")
    void isMembreInscrit_confirme_returnsTrue() {
        Membre m = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e = persistEvenement(o);
        persistInscription(m, e, "CONFIRMEE");

        boolean inscrit = inscriptionRepository.isMembreInscrit(m.getId(), e.getId());
        assertThat(inscrit).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("isMembreInscrit retourne false si membre non inscrit")
    void isMembreInscrit_nonInscrit_returnsFalse() {
        boolean inscrit = inscriptionRepository.isMembreInscrit(UUID.randomUUID(), UUID.randomUUID());
        assertThat(inscrit).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("softDelete marque l'inscription comme inactive")
    void softDelete_setsInactif() {
        Membre m = persistMembre();
        Organisation o = persistOrganisation();
        Evenement e = persistEvenement(o);
        InscriptionEvenement ie = persistInscription(m, e, "CONFIRMEE");

        inscriptionRepository.softDelete(ie);

        assertThat(ie.getActif()).isFalse();
    }
}
