package dev.lions.unionflow.server.entity;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;

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

/**
 * Tests de couverture complémentaires pour Evenement.
 * Couvre les branches manquantes de isTermine(), isEnCours(), getTauxRemplissage().
 */
@DisplayName("Evenement - couverture complémentaire")
class EvenementCoverageTest {

    private Evenement buildEvenement() {
        Evenement e = new Evenement();
        e.setTitre("Test Evenement");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setStatut("PLANIFIE");
        return e;
    }

    @Test
    @DisplayName("isTermine: true si dateFin est dans le passé (statut non TERMINE)")
    void isTermine_pastDateFin_returnsTrue() {
        Evenement e = buildEvenement();
        e.setDateDebut(LocalDateTime.now().minusDays(2));
        e.setDateFin(LocalDateTime.now().minusHours(1)); // dateFin passée
        e.setStatut("CONFIRME"); // pas TERMINE
        assertThat(e.isTermine()).isTrue();
    }

    @Test
    @DisplayName("isTermine: false si dateFin est dans le futur (statut non TERMINE)")
    void isTermine_futureDateFin_returnsFalse() {
        Evenement e = buildEvenement();
        e.setDateFin(LocalDateTime.now().plusHours(2)); // dateFin future
        e.setStatut("PLANIFIE");
        assertThat(e.isTermine()).isFalse();
    }

    @Test
    @DisplayName("isEnCours: false si dateDebut future")
    void isEnCours_futureDateDebut_returnsFalse() {
        Evenement e = buildEvenement();
        e.setDateDebut(LocalDateTime.now().plusHours(1));
        assertThat(e.isEnCours()).isFalse();
    }

    @Test
    @DisplayName("isEnCours: true si dateDebut passée et dateFin null (événement sans fin définie)")
    void isEnCours_noDateFin_returnsTrue() {
        Evenement e = buildEvenement();
        e.setDateDebut(LocalDateTime.now().minusHours(1));
        e.setDateFin(null); // pas de dateFin → toujours en cours si après dateDebut
        assertThat(e.isEnCours()).isTrue();
    }

    @Test
    @DisplayName("isEnCours: false si dateFin passée")
    void isEnCours_pastDateFin_returnsFalse() {
        Evenement e = buildEvenement();
        e.setDateDebut(LocalDateTime.now().minusHours(2));
        e.setDateFin(LocalDateTime.now().minusHours(1)); // dateFin passée
        assertThat(e.isEnCours()).isFalse();
    }

    @Test
    @DisplayName("getTauxRemplissage: null si capaciteMax est 0")
    void getTauxRemplissage_zeroCapaciteMax_returnsNull() {
        Evenement e = buildEvenement();
        e.setCapaciteMax(0);
        assertThat(e.getTauxRemplissage()).isNull();
    }

    @Test
    @DisplayName("getTauxRemplissage: retourne le pourcentage si inscriptions")
    void getTauxRemplissage_withInscriptions_returnsPercentage() {
        Evenement e = buildEvenement();
        e.setCapaciteMax(4);

        Membre m1 = new Membre();
        m1.setId(java.util.UUID.randomUUID());
        InscriptionEvenement i1 = new InscriptionEvenement();
        i1.setMembre(m1);
        i1.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        e.getInscriptions().add(i1);

        Membre m2 = new Membre();
        m2.setId(java.util.UUID.randomUUID());
        InscriptionEvenement i2 = new InscriptionEvenement();
        i2.setMembre(m2);
        i2.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        e.getInscriptions().add(i2);

        // 2 inscrits sur 4 = 50%
        assertThat(e.getTauxRemplissage()).isEqualTo(50.0);
    }

    @Test
    @DisplayName("getPlacesRestantes: 0 si nbInscrits >= capaciteMax")
    void getPlacesRestantes_full_returnsZero() {
        Evenement e = buildEvenement();
        e.setCapaciteMax(1);

        Membre m = new Membre();
        m.setId(java.util.UUID.randomUUID());
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        e.getInscriptions().add(i);

        // capaciteMax=1, inscrits=1 → placesRestantes=0
        assertThat(e.getPlacesRestantes()).isEqualTo(0);
    }

    @Test
    @DisplayName("isMemberInscrit: false si membre non inscrit mais d'autres sont inscrits")
    void isMemberInscrit_differentMembre_returnsFalse() {
        Evenement e = buildEvenement();

        Membre m = new Membre();
        m.setId(java.util.UUID.randomUUID());
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        e.getInscriptions().add(i);

        // Vérifie un autre membre (non inscrit)
        assertThat(e.isMemberInscrit(java.util.UUID.randomUUID())).isFalse();
    }

    @Test
    @DisplayName("isMemberInscrit: false si inscription non CONFIRMEE")
    void isMemberInscrit_nonConfirmee_returnsFalse() {
        Evenement e = buildEvenement();
        java.util.UUID membreId = java.util.UUID.randomUUID();

        Membre m = new Membre();
        m.setId(membreId);
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.ANNULEE.name()); // pas CONFIRMEE
        e.getInscriptions().add(i);

        assertThat(e.isMemberInscrit(membreId)).isFalse();
    }

    // ── Branches manquantes ───────────────────────────────────────────────────

    /**
     * Branch manquante dans isComplet() : capaciteMax == null → false.
     * Le code : `return capaciteMax != null && getNombreInscrits() >= capaciteMax`
     * → quand capaciteMax est null, la condition court-circuite (false).
     */
    @Test
    @DisplayName("isComplet: false si capaciteMax null (capacité illimitée)")
    void isComplet_capaciteMaxNull_returnsFalse() {
        Evenement e = buildEvenement();
        e.setCapaciteMax(null); // illimité → jamais complet

        assertThat(e.isComplet()).isFalse();
    }

    /**
     * Branch manquante dans getTauxRemplissage() : capaciteMax == null → null.
     * Le code : `if (capaciteMax == null || capaciteMax == 0) { return null; }`
     * → première condition (capaciteMax == null) doit retourner null.
     */
    @Test
    @DisplayName("getTauxRemplissage: null si capaciteMax est null")
    void getTauxRemplissage_capaciteMaxNull_returnsNull() {
        Evenement e = buildEvenement();
        e.setCapaciteMax(null);

        assertThat(e.getTauxRemplissage()).isNull();
    }

    /**
     * Branch manquante dans isMemberInscrit() : inscriptions == null → false.
     * Le code : `return inscriptions != null && inscriptions.stream()...`
     * → quand inscriptions est null, retourne false.
     */
    @Test
    @DisplayName("isMemberInscrit: false si inscriptions est null")
    void isMemberInscrit_inscriptionsNull_returnsFalse() {
        Evenement e = buildEvenement();
        e.setInscriptions(null);

        assertThat(e.isMemberInscrit(java.util.UUID.randomUUID())).isFalse();
    }

    /**
     * Branch manquante dans isOuvertAuxInscriptions() : quand capaciteMax != null
     * mais getNombreInscrits() < capaciteMax → ne retourne pas false (capacité non atteinte),
     * passe à la vérification du statut.
     * Couvre le chemin où la branche capacité est false (non bloquante).
     */
    @Test
    @DisplayName("isOuvertAuxInscriptions: true si capaciteMax définie mais non atteinte")
    void isOuvertAuxInscriptions_capaciteNonAtteinte_returnsTrue() {
        Evenement e = buildEvenement();
        e.setInscriptionRequise(true);
        e.setActif(true);
        e.setCapaciteMax(5); // capacité non atteinte (0 inscrits)

        assertThat(e.isOuvertAuxInscriptions()).isTrue();
    }
}
