package dev.lions.unionflow.server.entity;

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

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

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

@DisplayName("Organisation — couverture complète")
class OrganisationTest {

    // ─── Utilitaire ───────────────────────────────────────────────────────────

    private static Organisation baseOrganisation() {
        Organisation o = new Organisation();
        o.setNom("Club Lions Paris");
        o.setNomCourt("CL Paris");
        o.setTypeOrganisation("ASSOCIATION");
        o.setStatut("ACTIVE");
        o.setEmail("contact@club.fr");
        return o;
    }

    // ─── Getters / Setters ────────────────────────────────────────────────────

    @Nested
    @DisplayName("Getters et setters")
    class GettersSetters {

        @Test
        @DisplayName("Champs de base")
        void champsDeBase() {
            Organisation o = baseOrganisation();
            o.setTelephone("+33100000000");
            o.setDevise("XOF");
            o.setNombreMembres(50);
            o.setEstOrganisationRacine(true);
            o.setAccepteNouveauxMembres(true);

            assertThat(o.getNom()).isEqualTo("Club Lions Paris");
            assertThat(o.getNomCourt()).isEqualTo("CL Paris");
            assertThat(o.getStatut()).isEqualTo("ACTIVE");
            assertThat(o.getEmail()).isEqualTo("contact@club.fr");
            assertThat(o.getTelephone()).isEqualTo("+33100000000");
            assertThat(o.getNombreMembres()).isEqualTo(50);
            assertThat(o.getEstOrganisationRacine()).isTrue();
            assertThat(o.getAccepteNouveauxMembres()).isTrue();
        }

        @Test
        @DisplayName("description, numeroEnregistrement, dateFondation")
        void descriptionEtEnregistrement() {
            Organisation o = baseOrganisation();
            LocalDate fondation = LocalDate.of(2010, 6, 15);
            o.setDescription("Un club de bienfaisance");
            o.setNumeroEnregistrement("REG-2010-00123");
            o.setDateFondation(fondation);

            assertThat(o.getDescription()).isEqualTo("Un club de bienfaisance");
            assertThat(o.getNumeroEnregistrement()).isEqualTo("REG-2010-00123");
            assertThat(o.getDateFondation()).isEqualTo(fondation);
        }

        @Test
        @DisplayName("telephoneSecondaire et emailSecondaire")
        void contactsSecondaires() {
            Organisation o = baseOrganisation();
            o.setTelephoneSecondaire("+33100000099");
            o.setEmailSecondaire("info@club.fr");

            assertThat(o.getTelephoneSecondaire()).isEqualTo("+33100000099");
            assertThat(o.getEmailSecondaire()).isEqualTo("info@club.fr");
        }

        @Test
        @DisplayName("adresse, ville, region, pays, codePostal")
        void adresseComplete() {
            Organisation o = baseOrganisation();
            o.setAdresse("12 rue de la Paix");
            o.setVille("Abidjan");
            o.setRegion("Lagunes");
            o.setPays("Côte d'Ivoire");
            o.setCodePostal("01 BP 1234");

            assertThat(o.getAdresse()).isEqualTo("12 rue de la Paix");
            assertThat(o.getVille()).isEqualTo("Abidjan");
            assertThat(o.getRegion()).isEqualTo("Lagunes");
            assertThat(o.getPays()).isEqualTo("Côte d'Ivoire");
            assertThat(o.getCodePostal()).isEqualTo("01 BP 1234");
        }

        @Test
        @DisplayName("latitude et longitude")
        void coordonneesGeographiques() {
            Organisation o = baseOrganisation();
            o.setLatitude(new BigDecimal("5.354680"));
            o.setLongitude(new BigDecimal("-4.001430"));

            assertThat(o.getLatitude()).isEqualByComparingTo("5.354680");
            assertThat(o.getLongitude()).isEqualByComparingTo("-4.001430");
        }

        @Test
        @DisplayName("siteWeb, logo, reseauxSociaux")
        void webEtReseaux() {
            Organisation o = baseOrganisation();
            o.setSiteWeb("https://club-lions-paris.fr");
            o.setLogo("https://cdn.example.com/logo.png");
            o.setReseauxSociaux("{\"twitter\":\"@clublions\"}");

            assertThat(o.getSiteWeb()).isEqualTo("https://club-lions-paris.fr");
            assertThat(o.getLogo()).isEqualTo("https://cdn.example.com/logo.png");
            assertThat(o.getReseauxSociaux()).isEqualTo("{\"twitter\":\"@clublions\"}");
        }

        @Test
        @DisplayName("niveauHierarchique, cheminHierarchique, organisationParente")
        void hierarchie() {
            Organisation parent = baseOrganisation();
            parent.setId(UUID.randomUUID());

            Organisation enfant = baseOrganisation();
            enfant.setNom("Sous-club Lions");
            enfant.setEmail("sous@club.fr");
            enfant.setOrganisationParente(parent);
            enfant.setNiveauHierarchique(1);
            enfant.setCheminHierarchique("/" + parent.getId());
            enfant.setEstOrganisationRacine(false);

            assertThat(enfant.getOrganisationParente()).isEqualTo(parent);
            assertThat(enfant.getNiveauHierarchique()).isEqualTo(1);
            assertThat(enfant.getCheminHierarchique()).startsWith("/");
            assertThat(enfant.getEstOrganisationRacine()).isFalse();
        }

        @Test
        @DisplayName("nombreAdministrateurs")
        void nombreAdministrateurs() {
            Organisation o = baseOrganisation();
            o.setNombreAdministrateurs(5);
            assertThat(o.getNombreAdministrateurs()).isEqualTo(5);
        }

        @Test
        @DisplayName("budgetAnnuel, devise, cotisationObligatoire, montantCotisationAnnuelle")
        void finances() {
            Organisation o = baseOrganisation();
            o.setBudgetAnnuel(new BigDecimal("5000000.00"));
            o.setDevise("XOF");
            o.setCotisationObligatoire(true);
            o.setMontantCotisationAnnuelle(new BigDecimal("120000.00"));

            assertThat(o.getBudgetAnnuel()).isEqualByComparingTo("5000000.00");
            assertThat(o.getDevise()).isEqualTo("XOF");
            assertThat(o.getCotisationObligatoire()).isTrue();
            assertThat(o.getMontantCotisationAnnuelle()).isEqualByComparingTo("120000.00");
        }

        @Test
        @DisplayName("objectifs, activitesPrincipales, certifications, partenaires, notes")
        void informationsComplementaires() {
            Organisation o = baseOrganisation();
            o.setObjectifs("Aide humanitaire");
            o.setActivitesPrincipales("Collecte de fonds, bénévolat");
            o.setCertifications("ISO 9001");
            o.setPartenaires("Croix-Rouge, ONU");
            o.setNotes("Organisation fondée par des bénévoles");

            assertThat(o.getObjectifs()).isEqualTo("Aide humanitaire");
            assertThat(o.getActivitesPrincipales()).isEqualTo("Collecte de fonds, bénévolat");
            assertThat(o.getCertifications()).isEqualTo("ISO 9001");
            assertThat(o.getPartenaires()).isEqualTo("Croix-Rouge, ONU");
            assertThat(o.getNotes()).isEqualTo("Organisation fondée par des bénévoles");
        }

        @Test
        @DisplayName("organisationPublique")
        void organisationPublique() {
            Organisation o = baseOrganisation();
            o.setOrganisationPublique(false);
            assertThat(o.getOrganisationPublique()).isFalse();
            o.setOrganisationPublique(true);
            assertThat(o.getOrganisationPublique()).isTrue();
        }

        @Test
        @DisplayName("Relations : membresOrganisations, adresses, comptesWave")
        void relations() {
            Organisation o = baseOrganisation();
            o.setMembresOrganisations(new ArrayList<>());
            o.setAdresses(new ArrayList<>());
            o.setComptesWave(new ArrayList<>());

            assertThat(o.getMembresOrganisations()).isNotNull().isEmpty();
            assertThat(o.getAdresses()).isNotNull().isEmpty();
            assertThat(o.getComptesWave()).isNotNull().isEmpty();
        }

        @Test
        @DisplayName("Champs hérités de BaseEntity (id, dateCreation, creePar, modifiePar, version, actif)")
        void champsBaseEntity() {
            UUID id = UUID.randomUUID();
            Organisation o = baseOrganisation();
            o.setId(id);
            o.setCreePar("admin@unionflow.dev");
            o.setModifiePar("manager@unionflow.dev");
            o.setVersion(2L);
            o.setActif(true);

            assertThat(o.getId()).isEqualTo(id);
            assertThat(o.getCreePar()).isEqualTo("admin@unionflow.dev");
            assertThat(o.getModifiePar()).isEqualTo("manager@unionflow.dev");
            assertThat(o.getVersion()).isEqualTo(2L);
            assertThat(o.getActif()).isTrue();
        }
    }

    // ─── Builder ──────────────────────────────────────────────────────────────

    @Nested
    @DisplayName("Builder Lombok")
    class BuilderTest {

        @Test
        @DisplayName("Builder avec champs obligatoires")
        void builderChampsObligatoires() {
            Organisation o = Organisation.builder()
                    .nom("Association Solidarité")
                    .typeOrganisation("ASSOCIATION")
                    .statut("ACTIVE")
                    .email("contact@solidarite.ci")
                    .build();

            assertThat(o.getNom()).isEqualTo("Association Solidarité");
            assertThat(o.getTypeOrganisation()).isEqualTo("ASSOCIATION");
            assertThat(o.getStatut()).isEqualTo("ACTIVE");
            assertThat(o.getEmail()).isEqualTo("contact@solidarite.ci");
        }

        @Test
        @DisplayName("Builder : valeurs @Builder.Default")
        void builderDefaults() {
            Organisation o = Organisation.builder()
                    .nom("Coopérative Test")
                    .typeOrganisation("COOPERATIVE")
                    .statut("ACTIVE")
                    .email("coop@test.ci")
                    .build();

            assertThat(o.getNiveauHierarchique()).isEqualTo(0);
            assertThat(o.getEstOrganisationRacine()).isTrue();
            assertThat(o.getNombreMembres()).isEqualTo(0);
            assertThat(o.getNombreAdministrateurs()).isEqualTo(0);
            assertThat(o.getDevise()).isEqualTo("XOF");
            assertThat(o.getCotisationObligatoire()).isFalse();
            assertThat(o.getOrganisationPublique()).isTrue();
            assertThat(o.getAccepteNouveauxMembres()).isTrue();
            assertThat(o.getMembresOrganisations()).isNotNull().isEmpty();
            assertThat(o.getAdresses()).isNotNull().isEmpty();
            assertThat(o.getComptesWave()).isNotNull().isEmpty();
        }

        @Test
        @DisplayName("Builder avec tous les champs facultatifs")
        void builderChampsOptionnels() {
            LocalDate fondation = LocalDate.of(2000, 1, 1);
            Organisation parent = baseOrganisation();
            parent.setId(UUID.randomUUID());

            Organisation o = Organisation.builder()
                    .nom("Lions Club Abidjan")
                    .nomCourt("LCA")
                    .typeOrganisation("LIONS")
                    .statut("ACTIVE")
                    .email("lions@abidjan.ci")
                    .telephone("+22520000000")
                    .telephoneSecondaire("+22521000000")
                    .emailSecondaire("info@lions.ci")
                    .description("Club Lions International")
                    .dateFondation(fondation)
                    .numeroEnregistrement("LCA-2000-001")
                    .adresse("Plateau, Av. de la République")
                    .ville("Abidjan")
                    .region("Lagunes")
                    .pays("Côte d'Ivoire")
                    .codePostal("01 BP 100")
                    .latitude(new BigDecimal("5.354"))
                    .longitude(new BigDecimal("-4.001"))
                    .siteWeb("https://lions.ci")
                    .logo("https://cdn.lions.ci/logo.png")
                    .reseauxSociaux("{\"facebook\":\"lionsabidjan\"}")
                    .organisationParente(parent)
                    .niveauHierarchique(1)
                    .estOrganisationRacine(false)
                    .cheminHierarchique("/" + parent.getId())
                    .nombreMembres(120)
                    .nombreAdministrateurs(8)
                    .budgetAnnuel(new BigDecimal("10000000.00"))
                    .devise("XOF")
                    .cotisationObligatoire(true)
                    .montantCotisationAnnuelle(new BigDecimal("100000.00"))
                    .objectifs("Service humanitaire")
                    .activitesPrincipales("Actions sociales")
                    .certifications("Lions International")
                    .partenaires("UNICEF")
                    .notes("Fondé par des membres engagés")
                    .organisationPublique(true)
                    .accepteNouveauxMembres(true)
                    .build();

            assertThat(o.getNomCourt()).isEqualTo("LCA");
            assertThat(o.getDescription()).isEqualTo("Club Lions International");
            assertThat(o.getDateFondation()).isEqualTo(fondation);
            assertThat(o.getNumeroEnregistrement()).isEqualTo("LCA-2000-001");
            assertThat(o.getVille()).isEqualTo("Abidjan");
            assertThat(o.getPays()).isEqualTo("Côte d'Ivoire");
            assertThat(o.getLatitude()).isEqualByComparingTo("5.354");
            assertThat(o.getLongitude()).isEqualByComparingTo("-4.001");
            assertThat(o.getSiteWeb()).isEqualTo("https://lions.ci");
            assertThat(o.getOrganisationParente()).isEqualTo(parent);
            assertThat(o.getNiveauHierarchique()).isEqualTo(1);
            assertThat(o.getEstOrganisationRacine()).isFalse();
            assertThat(o.getNombreMembres()).isEqualTo(120);
            assertThat(o.getBudgetAnnuel()).isEqualByComparingTo("10000000.00");
            assertThat(o.getCotisationObligatoire()).isTrue();
            assertThat(o.getMontantCotisationAnnuelle()).isEqualByComparingTo("100000.00");
            assertThat(o.getObjectifs()).isEqualTo("Service humanitaire");
        }
    }

    // ─── Méthodes métier ──────────────────────────────────────────────────────

    @Nested
    @DisplayName("Méthodes métier")
    class MethodesMetier {

        @Test
        @DisplayName("getNomComplet() avec nomCourt non vide")
        void getNomComplet_avecNomCourt() {
            Organisation o = baseOrganisation();
            o.setNomCourt("CL Paris");
            assertThat(o.getNomComplet()).isEqualTo("Club Lions Paris (CL Paris)");
        }

        @Test
        @DisplayName("getNomComplet() avec nomCourt=null")
        void getNomComplet_nomCourtNull() {
            Organisation o = baseOrganisation();
            o.setNomCourt(null);
            assertThat(o.getNomComplet()).isEqualTo("Club Lions Paris");
        }

        @Test
        @DisplayName("getNomComplet() avec nomCourt vide ('')")
        void getNomComplet_nomCourtVide() {
            Organisation o = baseOrganisation();
            o.setNomCourt("");
            assertThat(o.getNomComplet()).isEqualTo("Club Lions Paris");
        }

        @Test
        @DisplayName("getAncienneteAnnees() avec dateFondation=null retourne 0")
        void anciennete_dateFondationNull_retourneZero() {
            Organisation o = baseOrganisation();
            o.setDateFondation(null);
            assertThat(o.getAncienneteAnnees()).isEqualTo(0);
        }

        @Test
        @DisplayName("getAncienneteAnnees() avec dateFondation il y a 5 ans retourne 5")
        void anciennete_cincAns_retourneCinq() {
            Organisation o = baseOrganisation();
            o.setDateFondation(LocalDate.now().minusYears(5));
            assertThat(o.getAncienneteAnnees()).isEqualTo(5);
        }

        @Test
        @DisplayName("isRecente() retourne false si ancienneté >= 2 ans")
        void isRecente_ancienneOrganisation_retourneFalse() {
            Organisation o = baseOrganisation();
            o.setDateFondation(LocalDate.now().minusYears(3));
            assertThat(o.isRecente()).isFalse();
        }

        @Test
        @DisplayName("isRecente() retourne true si ancienneté < 2 ans")
        void isRecente_jeuneOrganisation_retourneTrue() {
            Organisation o = baseOrganisation();
            o.setDateFondation(LocalDate.now().minusYears(1));
            assertThat(o.isRecente()).isTrue();
        }

        @Test
        @DisplayName("isRecente() retourne true si dateFondation=null (ancienneté=0 < 2)")
        void isRecente_dateFondationNull_retourneTrue() {
            Organisation o = baseOrganisation();
            o.setDateFondation(null);
            assertThat(o.isRecente()).isTrue();
        }

        @Test
        @DisplayName("isActive() retourne true si statut=ACTIVE et actif=true")
        void isActive_activeEtActif_retourneTrue() {
            Organisation o = baseOrganisation();
            o.setStatut("ACTIVE");
            o.setActif(true);
            assertThat(o.isActive()).isTrue();
        }

        @Test
        @DisplayName("isActive() retourne false si statut=SUSPENDUE")
        void isActive_suspendue_retourneFalse() {
            Organisation o = baseOrganisation();
            o.setStatut("SUSPENDUE");
            o.setActif(true);
            assertThat(o.isActive()).isFalse();
        }

        @Test
        @DisplayName("isActive() retourne false si actif=false")
        void isActive_inactif_retourneFalse() {
            Organisation o = baseOrganisation();
            o.setStatut("ACTIVE");
            o.setActif(false);
            assertThat(o.isActive()).isFalse();
        }

        @Test
        @DisplayName("isActive() retourne false si actif=null")
        void isActive_actifNull_retourneFalse() {
            Organisation o = baseOrganisation();
            o.setStatut("ACTIVE");
            o.setActif(null);
            assertThat(o.isActive()).isFalse();
        }

        @Test
        @DisplayName("ajouterMembre() incrémente nombreMembres")
        void ajouterMembre_incremente() {
            Organisation o = baseOrganisation();
            o.setNombreMembres(10);
            o.ajouterMembre();
            assertThat(o.getNombreMembres()).isEqualTo(11);
        }

        @Test
        @DisplayName("ajouterMembre() avec nombreMembres=null initialise à 1")
        void ajouterMembre_null_initialiseeA1() {
            Organisation o = baseOrganisation();
            o.setNombreMembres(null);
            o.ajouterMembre();
            assertThat(o.getNombreMembres()).isEqualTo(1);
        }

        @Test
        @DisplayName("retirerMembre() décrémente nombreMembres")
        void retirerMembre_decremente() {
            Organisation o = baseOrganisation();
            o.setNombreMembres(10);
            o.retirerMembre();
            assertThat(o.getNombreMembres()).isEqualTo(9);
        }

        @Test
        @DisplayName("retirerMembre() ne passe pas sous 0 quand nombreMembres=0")
        void retirerMembre_zero_resteAZero() {
            Organisation o = baseOrganisation();
            o.setNombreMembres(0);
            o.retirerMembre();
            assertThat(o.getNombreMembres()).isEqualTo(0);
        }

        @Test
        @DisplayName("retirerMembre() ne fait rien si nombreMembres=null")
        void retirerMembre_null_neFaitRien() {
            Organisation o = baseOrganisation();
            o.setNombreMembres(null);
            o.retirerMembre();
            assertThat(o.getNombreMembres()).isNull();
        }

        @Test
        @DisplayName("activer() met statut=ACTIVE et actif=true")
        void activer_metAJourStatutEtActif() {
            Organisation o = baseOrganisation();
            o.setStatut("SUSPENDUE");
            o.setActif(false);
            o.activer("admin@unionflow.dev");

            assertThat(o.getStatut()).isEqualTo("ACTIVE");
            assertThat(o.getActif()).isTrue();
            assertThat(o.getModifiePar()).isEqualTo("admin@unionflow.dev");
            assertThat(o.getDateModification()).isNotNull();
        }

        @Test
        @DisplayName("suspendre() met statut=SUSPENDUE et accepteNouveauxMembres=false")
        void suspendre_metAJourStatutEtMembres() {
            Organisation o = baseOrganisation();
            o.setAccepteNouveauxMembres(true);
            o.suspendre("manager@unionflow.dev");

            assertThat(o.getStatut()).isEqualTo("SUSPENDUE");
            assertThat(o.getAccepteNouveauxMembres()).isFalse();
            assertThat(o.getModifiePar()).isEqualTo("manager@unionflow.dev");
        }

        @Test
        @DisplayName("dissoudre() met statut=DISSOUTE, actif=false, accepteNouveauxMembres=false")
        void dissoudre_metAJourTousLesChamps() {
            Organisation o = baseOrganisation();
            o.setActif(true);
            o.setAccepteNouveauxMembres(true);
            o.dissoudre("liquidateur@unionflow.dev");

            assertThat(o.getStatut()).isEqualTo("DISSOUTE");
            assertThat(o.getActif()).isFalse();
            assertThat(o.getAccepteNouveauxMembres()).isFalse();
            assertThat(o.getModifiePar()).isEqualTo("liquidateur@unionflow.dev");
        }
    }

    // ─── @PrePersist onCreate ─────────────────────────────────────────────────

    @Nested
    @DisplayName("@PrePersist onCreate()")
    class OnCreate {

        @Test
        @DisplayName("statut=null est initialisé à ACTIVE")
        void statut_null_initialiseeAACtive() {
            Organisation o = baseOrganisation();
            o.setStatut(null);
            o.onCreate();

            assertThat(o.getStatut()).isEqualTo("ACTIVE");
        }

        @Test
        @DisplayName("statut déjà défini n'est pas écrasé")
        void statut_dejaDefini_nonEcrase() {
            Organisation o = baseOrganisation();
            o.setStatut("SUSPENDUE");
            o.onCreate();

            assertThat(o.getStatut()).isEqualTo("SUSPENDUE");
        }

        @Test
        @DisplayName("typeOrganisation=null est initialisé à ASSOCIATION")
        void typeOrganisation_null_initialiseeAAssociation() {
            Organisation o = baseOrganisation();
            o.setTypeOrganisation(null);
            o.onCreate();

            assertThat(o.getTypeOrganisation()).isEqualTo("ASSOCIATION");
        }

        @Test
        @DisplayName("typeOrganisation déjà défini n'est pas écrasé")
        void typeOrganisation_dejaDefini_nonEcrase() {
            Organisation o = baseOrganisation();
            o.setTypeOrganisation("COOPERATIVE");
            o.onCreate();

            assertThat(o.getTypeOrganisation()).isEqualTo("COOPERATIVE");
        }

        @Test
        @DisplayName("devise=null est initialisé à XOF")
        void devise_null_initialiseeAXof() {
            Organisation o = baseOrganisation();
            o.setDevise(null);
            o.onCreate();

            assertThat(o.getDevise()).isEqualTo("XOF");
        }

        @Test
        @DisplayName("devise déjà défini n'est pas écrasé")
        void devise_dejaDefinie_nonEcrasee() {
            Organisation o = baseOrganisation();
            o.setDevise("EUR");
            o.onCreate();

            assertThat(o.getDevise()).isEqualTo("EUR");
        }

        @Test
        @DisplayName("niveauHierarchique=null est initialisé à 0")
        void niveauHierarchique_null_initialiseeAZero() {
            Organisation o = baseOrganisation();
            o.setNiveauHierarchique(null);
            o.onCreate();

            assertThat(o.getNiveauHierarchique()).isEqualTo(0);
        }

        @Test
        @DisplayName("estOrganisationRacine=null sans parent est initialisé à true")
        void estOrganisationRacine_null_sansParent_initialiseeATrue() {
            Organisation o = baseOrganisation();
            o.setEstOrganisationRacine(null);
            o.setOrganisationParente(null);
            o.onCreate();

            assertThat(o.getEstOrganisationRacine()).isTrue();
        }

        @Test
        @DisplayName("estOrganisationRacine=null avec parent est initialisé à false")
        void estOrganisationRacine_null_avecParent_initialiseeAFalse() {
            Organisation parent = baseOrganisation();
            parent.setId(UUID.randomUUID());

            Organisation o = baseOrganisation();
            o.setNom("Sous-club");
            o.setEmail("sous@club.fr");
            o.setEstOrganisationRacine(null);
            o.setOrganisationParente(parent);
            o.onCreate();

            assertThat(o.getEstOrganisationRacine()).isFalse();
        }

        @Test
        @DisplayName("estOrganisationRacine déjà défini n'est pas écrasé")
        void estOrganisationRacine_dejaDefini_nonEcrase() {
            Organisation o = baseOrganisation();
            o.setEstOrganisationRacine(false);
            o.setOrganisationParente(null);
            o.onCreate();

            assertThat(o.getEstOrganisationRacine()).isFalse();
        }

        @Test
        @DisplayName("nombreMembres=null est initialisé à 0")
        void nombreMembres_null_initialiseeAZero() {
            Organisation o = baseOrganisation();
            o.setNombreMembres(null);
            o.onCreate();

            assertThat(o.getNombreMembres()).isEqualTo(0);
        }

        @Test
        @DisplayName("nombreAdministrateurs=null est initialisé à 0")
        void nombreAdministrateurs_null_initialiseeAZero() {
            Organisation o = baseOrganisation();
            o.setNombreAdministrateurs(null);
            o.onCreate();

            assertThat(o.getNombreAdministrateurs()).isEqualTo(0);
        }

        @Test
        @DisplayName("organisationPublique=null est initialisé à true")
        void organisationPublique_null_initialiseeATrue() {
            Organisation o = baseOrganisation();
            o.setOrganisationPublique(null);
            o.onCreate();

            assertThat(o.getOrganisationPublique()).isTrue();
        }

        @Test
        @DisplayName("accepteNouveauxMembres=null est initialisé à true")
        void accepteNouveauxMembres_null_initialiseeATrue() {
            Organisation o = baseOrganisation();
            o.setAccepteNouveauxMembres(null);
            o.onCreate();

            assertThat(o.getAccepteNouveauxMembres()).isTrue();
        }

        @Test
        @DisplayName("cotisationObligatoire=null est initialisé à false")
        void cotisationObligatoire_null_initialiseeAFalse() {
            Organisation o = baseOrganisation();
            o.setCotisationObligatoire(null);
            o.onCreate();

            assertThat(o.getCotisationObligatoire()).isFalse();
        }

        @Test
        @DisplayName("onCreate() initialise aussi dateCreation et actif via BaseEntity")
        void onCreateInitialiseBaseEntity() {
            Organisation o = baseOrganisation();
            o.setDateCreation(null);
            o.setActif(null);
            o.onCreate();

            assertThat(o.getDateCreation()).isNotNull();
            assertThat(o.getActif()).isTrue();
        }

        @Test
        @DisplayName("onCreate() avec tous les champs null initialise toutes les valeurs par défaut")
        void onCreateTousChampNulls_initialiseDefaults() {
            Organisation o = new Organisation();
            o.setNom("Test Org");
            o.setEmail("test@org.ci");
            // Tous les @Builder.Default sont null via new()
            o.setStatut(null);
            o.setTypeOrganisation(null);
            o.setDevise(null);
            o.setNiveauHierarchique(null);
            o.setEstOrganisationRacine(null);
            o.setOrganisationParente(null);
            o.setNombreMembres(null);
            o.setNombreAdministrateurs(null);
            o.setOrganisationPublique(null);
            o.setAccepteNouveauxMembres(null);
            o.setCotisationObligatoire(null);
            o.onCreate();

            assertThat(o.getStatut()).isEqualTo("ACTIVE");
            assertThat(o.getTypeOrganisation()).isEqualTo("ASSOCIATION");
            assertThat(o.getDevise()).isEqualTo("XOF");
            assertThat(o.getNiveauHierarchique()).isEqualTo(0);
            assertThat(o.getEstOrganisationRacine()).isTrue();
            assertThat(o.getNombreMembres()).isEqualTo(0);
            assertThat(o.getNombreAdministrateurs()).isEqualTo(0);
            assertThat(o.getOrganisationPublique()).isTrue();
            assertThat(o.getAccepteNouveauxMembres()).isTrue();
            assertThat(o.getCotisationObligatoire()).isFalse();
        }
    }

    // ─── Equals / HashCode / toString ────────────────────────────────────────

    @Nested
    @DisplayName("equals, hashCode et toString")
    class EgaliteEtToString {

        @Test
        @DisplayName("Deux instances avec le même id sont égales")
        void equals_memeId_egales() {
            UUID id = UUID.randomUUID();
            Organisation a = buildOrganisation(id, "N", "e@e.com");
            Organisation b = buildOrganisation(id, "N", "e@e.com");

            assertThat(a).isEqualTo(b);
            assertThat(a.hashCode()).isEqualTo(b.hashCode());
        }

        @Test
        @DisplayName("Deux instances avec des id différents ne sont pas égales")
        void equals_idsDifferents_pasEgales() {
            Organisation a = buildOrganisation(UUID.randomUUID(), "N1", "e1@e.com");
            Organisation b = buildOrganisation(UUID.randomUUID(), "N2", "e2@e.com");

            assertThat(a).isNotEqualTo(b);
        }

        @Test
        @DisplayName("toString() n'est pas null ni vide")
        void toString_nonNullNonVide() {
            Organisation o = baseOrganisation();
            assertThat(o.toString()).isNotNull().isNotEmpty();
        }

        @Test
        @DisplayName("toString() contient le nom de l'organisation")
        void toString_contientNom() {
            Organisation o = baseOrganisation();
            assertThat(o.toString()).contains("Club Lions Paris");
        }

        private Organisation buildOrganisation(UUID id, String nom, String email) {
            Organisation o = new Organisation();
            o.setId(id);
            o.setNom(nom);
            o.setTypeOrganisation("X");
            o.setStatut("ACTIVE");
            o.setEmail(email);
            return o;
        }
    }

    // ─── marquerCommeModifie (hérité de BaseEntity) ──────────────────────────

    @Nested
    @DisplayName("marquerCommeModifie() — hérité de BaseEntity")
    class MarquerCommeModifie {

        @Test
        @DisplayName("marquerCommeModifie() met à jour dateModification et modifiePar")
        void marquerCommeModifie_metAJourChamps() {
            Organisation o = baseOrganisation();
            o.marquerCommeModifie("operateur@unionflow.dev");

            assertThat(o.getDateModification()).isNotNull();
            assertThat(o.getModifiePar()).isEqualTo("operateur@unionflow.dev");
        }
    }
}
