package dev.lions.unionflow.server.entity;

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

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("Evenement")
class EvenementTest {

    @Test
    @DisplayName("getters/setters")
    void gettersSetters() {
        Evenement e = new Evenement();
        e.setTitre("AG 2025");
        e.setDescription("Assemblée générale");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setDateFin(LocalDateTime.now().plusDays(1).plusHours(2));
        e.setLieu("Salle A");
        e.setTypeEvenement("ASSEMBLEE_GENERALE");
        e.setStatut("PLANIFIE");
        e.setCapaciteMax(100);
        e.setPrix(new BigDecimal("0"));
        e.setInscriptionRequise(true);
        e.setVisiblePublic(true);

        assertThat(e.getTitre()).isEqualTo("AG 2025");
        assertThat(e.getDescription()).isEqualTo("Assemblée générale");
        assertThat(e.getLieu()).isEqualTo("Salle A");
        assertThat(e.getStatut()).isEqualTo("PLANIFIE");
        assertThat(e.getCapaciteMax()).isEqualTo(100);
        assertThat(e.getInscriptionRequise()).isTrue();
    }

    @Test
    @DisplayName("getNombreInscrits: 0 quand pas d'inscriptions")
    void getNombreInscrits() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        assertThat(e.getNombreInscrits()).isEqualTo(0);
    }

    @Test
    @DisplayName("isComplet: true si capaciteMax atteinte")
    void isComplet() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.setCapaciteMax(2);
        InscriptionEvenement i1 = new InscriptionEvenement();
        i1.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        i1.setMembre(new Membre());
        InscriptionEvenement i2 = new InscriptionEvenement();
        i2.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        i2.setMembre(new Membre());
        e.getInscriptions().add(i1);
        e.getInscriptions().add(i2);
        assertThat(e.isComplet()).isTrue();
        e.getInscriptions().remove(1);
        assertThat(e.isComplet()).isFalse();
    }

    @Test
    @DisplayName("getDureeEnHeures")
    void getDureeEnHeures() {
        LocalDateTime debut = LocalDateTime.of(2025, 6, 1, 10, 0);
        LocalDateTime fin = LocalDateTime.of(2025, 6, 1, 12, 0);
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(debut);
        e.setDateFin(fin);
        assertThat(e.getDureeEnHeures()).isEqualTo(2L);
        e.setDateFin(null);
        assertThat(e.getDureeEnHeures()).isNull();
    }

    @Test
    @DisplayName("getPlacesRestantes")
    void getPlacesRestantes() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.setCapaciteMax(10);
        assertThat(e.getPlacesRestantes()).isEqualTo(10);
        e.setCapaciteMax(null);
        assertThat(e.getPlacesRestantes()).isNull();
    }

    @Test
    @DisplayName("isTermine: true si statut TERMINE")
    void isTermine_true_statutTermine() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().minusDays(2));
        e.setDateFin(LocalDateTime.now().minusDays(1));
        e.setStatut("TERMINE");
        assertThat(e.isTermine()).isTrue();
    }

    @Test
    @DisplayName("isTermine: false si statut PLANIFIE et dateFin null")
    void isTermine_false() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setDateFin(null);
        e.setStatut("PLANIFIE");
        assertThat(e.isTermine()).isFalse();
    }

    @Test
    @DisplayName("getTauxRemplissage")
    void getTauxRemplissage() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.setCapaciteMax(10);
        assertThat(e.getTauxRemplissage()).isEqualTo(0.0);
        e.setCapaciteMax(0);
        assertThat(e.getTauxRemplissage()).isNull();
    }

    @Test
    @DisplayName("TypeEvenement enum values and getLibelle")
    void typeEvenementEnum() {
        assertThat(Evenement.TypeEvenement.ASSEMBLEE_GENERALE.getLibelle()).isEqualTo("Assemblée Générale");
        assertThat(Evenement.TypeEvenement.REUNION.getLibelle()).isEqualTo("Réunion");
        assertThat(Evenement.TypeEvenement.FORMATION.getLibelle()).isEqualTo("Formation");
        assertThat(Evenement.TypeEvenement.CONFERENCE.getLibelle()).isEqualTo("Conférence");
        assertThat(Evenement.TypeEvenement.ATELIER.getLibelle()).isEqualTo("Atelier");
        assertThat(Evenement.TypeEvenement.SEMINAIRE.getLibelle()).isEqualTo("Séminaire");
        assertThat(Evenement.TypeEvenement.EVENEMENT_SOCIAL.getLibelle()).isEqualTo("Événement Social");
        assertThat(Evenement.TypeEvenement.MANIFESTATION.getLibelle()).isEqualTo("Manifestation");
        assertThat(Evenement.TypeEvenement.CELEBRATION.getLibelle()).isEqualTo("Célébration");
        assertThat(Evenement.TypeEvenement.AUTRE.getLibelle()).isEqualTo("Autre");
        assertThat(Evenement.TypeEvenement.values()).hasSize(10);
    }

    @Test
    @DisplayName("StatutEvenement enum values and getLibelle")
    void statutEvenementEnum() {
        assertThat(Evenement.StatutEvenement.PLANIFIE.getLibelle()).isEqualTo("Planifié");
        assertThat(Evenement.StatutEvenement.CONFIRME.getLibelle()).isEqualTo("Confirmé");
        assertThat(Evenement.StatutEvenement.EN_COURS.getLibelle()).isEqualTo("En cours");
        assertThat(Evenement.StatutEvenement.TERMINE.getLibelle()).isEqualTo("Terminé");
        assertThat(Evenement.StatutEvenement.ANNULE.getLibelle()).isEqualTo("Annulé");
        assertThat(Evenement.StatutEvenement.REPORTE.getLibelle()).isEqualTo("Reporté");
        assertThat(Evenement.StatutEvenement.values()).hasSize(6);
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        LocalDateTime dt = LocalDateTime.now();
        Evenement a = new Evenement();
        a.setId(id);
        a.setTitre("Ev");
        a.setDateDebut(dt);
        a.setStatut("PLANIFIE");
        Evenement b = new Evenement();
        b.setId(id);
        b.setTitre("Ev");
        b.setDateDebut(dt);
        b.setStatut("PLANIFIE");
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        assertThat(e.toString()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: false si inscription non requise")
    void isOuvertAuxInscriptions_false_sansInscription() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setInscriptionRequise(false);
        e.setStatut("PLANIFIE");
        e.setActif(true);
        assertThat(e.isOuvertAuxInscriptions()).isFalse();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: true si ouvert")
    void isOuvertAuxInscriptions_true() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setDateLimiteInscription(LocalDateTime.now().plusDays(1));
        e.setInscriptionRequise(true);
        e.setStatut("PLANIFIE");
        e.setActif(true);
        assertThat(e.isOuvertAuxInscriptions()).isTrue();
    }

    @Test
    @DisplayName("isEnCours: true si entre dateDebut et dateFin")
    void isEnCours_true() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().minusHours(1));
        e.setDateFin(LocalDateTime.now().plusHours(1));
        assertThat(e.isEnCours()).isTrue();
    }

    @Test
    @DisplayName("isMemberInscrit: true si membre confirmé")
    void isMemberInscrit_true() {
        UUID membreId = UUID.randomUUID();
        Membre m = new Membre();
        m.setId(membreId);
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.getInscriptions().add(i);
        assertThat(e.isMemberInscrit(membreId)).isTrue();
    }

    @Test
    @DisplayName("isMemberInscrit: false si pas d'inscriptions")
    void isMemberInscrit_false() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        assertThat(e.isMemberInscrit(UUID.randomUUID())).isFalse();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: false si actif=false")
    void isOuvertAuxInscriptions_false_actifFalse() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setInscriptionRequise(true);
        e.setStatut("PLANIFIE");
        e.setActif(false);
        assertThat(e.isOuvertAuxInscriptions()).isFalse();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: false si date limite dépassée")
    void isOuvertAuxInscriptions_false_dateLimitePassed() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setDateLimiteInscription(LocalDateTime.now().minusDays(1));
        e.setInscriptionRequise(true);
        e.setStatut("PLANIFIE");
        e.setActif(true);
        assertThat(e.isOuvertAuxInscriptions()).isFalse();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: false si événement déjà commencé")
    void isOuvertAuxInscriptions_false_dateDebutPassed() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().minusHours(1));
        e.setInscriptionRequise(true);
        e.setStatut("PLANIFIE");
        e.setActif(true);
        assertThat(e.isOuvertAuxInscriptions()).isFalse();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: false si capacité atteinte")
    void isOuvertAuxInscriptions_false_capacitePleine() {
        Membre m = new Membre();
        m.setId(UUID.randomUUID());
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());

        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setInscriptionRequise(true);
        e.setStatut("PLANIFIE");
        e.setActif(true);
        e.setCapaciteMax(1);
        e.getInscriptions().add(i);
        assertThat(e.isOuvertAuxInscriptions()).isFalse();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: true si statut CONFIRME")
    void isOuvertAuxInscriptions_true_statutConfirme() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setInscriptionRequise(true);
        e.setStatut("CONFIRME");
        e.setActif(true);
        assertThat(e.isOuvertAuxInscriptions()).isTrue();
    }

    @Test
    @DisplayName("getNombreInscrits: 0 si inscriptions null")
    void getNombreInscrits_inscriptionsNull_returnsZero() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.setInscriptions(null);
        assertThat(e.getNombreInscrits()).isEqualTo(0);
    }

    @Test
    @DisplayName("isTermine: true si dateFin passée et statut non TERMINE")
    void isTermine_true_datefinPassed() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().minusDays(3));
        e.setDateFin(LocalDateTime.now().minusDays(1));
        e.setStatut("PLANIFIE");
        assertThat(e.isTermine()).isTrue();
    }

    @Test
    @DisplayName("isOuvertAuxInscriptions: false si statut ni PLANIFIE ni CONFIRME")
    void isOuvertAuxInscriptions_false_statutAutre() {
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now().plusDays(1));
        e.setInscriptionRequise(true);
        e.setStatut("EN_COURS");
        e.setActif(true);
        assertThat(e.isOuvertAuxInscriptions()).isFalse();
    }

    @Test
    @DisplayName("isMemberInscrit: false si membre non confirmé")
    void isMemberInscrit_false_notConfirmed() {
        UUID membreId = UUID.randomUUID();
        Membre m = new Membre();
        m.setId(membreId);
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.EN_ATTENTE.name());
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.getInscriptions().add(i);
        assertThat(e.isMemberInscrit(membreId)).isFalse();
    }

    @Test
    @DisplayName("isMemberInscrit: false si mauvais id membre")
    void isMemberInscrit_false_wrongId() {
        UUID membreId = UUID.randomUUID();
        Membre m = new Membre();
        m.setId(UUID.randomUUID()); // different id
        InscriptionEvenement i = new InscriptionEvenement();
        i.setMembre(m);
        i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
        Evenement e = new Evenement();
        e.setTitre("Ev");
        e.setDateDebut(LocalDateTime.now());
        e.getInscriptions().add(i);
        assertThat(e.isMemberInscrit(membreId)).isFalse();
    }
}
