package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.adresse.request.CreateAdresseRequest;
import dev.lions.unionflow.server.api.dto.adresse.request.UpdateAdresseRequest;
import dev.lions.unionflow.server.api.dto.adresse.response.AdresseResponse;
import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.EvenementRepository;
import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@QuarkusTest
class AdresseServiceTest {

    @Inject
    AdresseService adresseService;

    @Inject
    MembreService membreService;

    @Inject
    OrganisationService organisationService;

    @Inject
    EvenementRepository evenementRepository;

    private Membre testMembre;
    private Organisation testOrganisation;

    @BeforeEach
    void setup() {
        testOrganisation = new Organisation();
        testOrganisation.setNom("Lions Club Adresse " + UUID.randomUUID());
        testOrganisation.setEmail("adr-" + UUID.randomUUID() + "@test.com");
        testOrganisation.setTypeOrganisation("CLUB");
        testOrganisation.setStatut("ACTIVE");
        testOrganisation.setActif(true);
        organisationService.creerOrganisation(testOrganisation, "admin@test.com");

        testMembre = new Membre();
        testMembre.setPrenom("Jean");
        testMembre.setNom("Domicile");
        testMembre.setEmail("jean.dom-" + UUID.randomUUID() + "@test.com");
        testMembre.setNumeroMembre("M-" + UUID.randomUUID().toString().substring(0, 8));
        testMembre.setDateNaissance(LocalDate.of(1980, 1, 1));
        testMembre.setStatutCompte("ACTIF");
        testMembre.setActif(true);
        membreService.creerMembre(testMembre);
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec données valides crée l'adresse")
    void creerAdresse_validRequest_createsAdresse() {
        CreateAdresseRequest request = new CreateAdresseRequest(
                "DOMICILE",
                "123 Rue de la République",
                "Appt 4B",
                "75001",
                "Paris",
                "IDF",
                "France",
                null,
                null,
                true,
                "Maison",
                "Près du parc",
                null,
                testMembre.getId(),
                null);

        AdresseResponse response = adresseService.creerAdresse(request);

        assertThat(response).isNotNull();
        assertThat(response.getVille()).isEqualTo("Paris");
        assertThat(response.getPrincipale()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourAdresse modifie les données")
    void mettreAJourAdresse_updatesData() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "BUREAU", "Ancienne Rue", null, "00000", "Ville", "Reg", "Pays", null, null, true,
                "Bureau", null, null,
                testMembre.getId(), null);
        AdresseResponse created = adresseService.creerAdresse(create);

        UpdateAdresseRequest update = new UpdateAdresseRequest(
                null,              // typeAdresse
                "Nouvelle Rue",    // adresse
                null,              // complementAdresse
                "11111",           // codePostal
                "Nouvelle Ville",  // ville
                null,              // region
                null,              // pays
                null,              // latitude
                null,              // longitude
                null,              // principale
                "Nouveau Bureau",  // libelle
                "Notes",           // notes
                null,              // organisationId
                null,              // membreId
                null);             // evenementId

        AdresseResponse result = adresseService.mettreAJourAdresse(created.getId(), update);

        assertThat(result.getAdresse()).isEqualTo("Nouvelle Rue");
        assertThat(result.getVille()).isEqualTo("Nouvelle Ville");
    }

    @Test
    @TestTransaction
    @DisplayName("desactive les autres adresses principales")
    void desactiverAutresPrincipales_works() {
        adresseService.creerAdresse(new CreateAdresseRequest(
                "DOMICILE", "Rue 1", null, "75001", "Paris", null, "France", null, null, true, "A1",
                null, null,
                testMembre.getId(), null));

        AdresseResponse a2 = adresseService.creerAdresse(new CreateAdresseRequest(
                "BUREAU", "Rue 2", null, "75002", "Paris", null, "France", null, null, true, "A2", null,
                null,
                testMembre.getId(), null));

        assertThat(a2.getPrincipale()).isTrue();

        AdresseResponse a1 = adresseService.trouverParMembre(testMembre.getId()).stream()
                .filter(a -> !a.getId().equals(a2.getId()))
                .findFirst().orElseThrow();

        assertThat(a1.getPrincipale()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParId: retourne l'adresse créée")
    void trouverParId_existant_returnsAdresse() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "DOMICILE", "Rue Trouvable", null, "11111", "TrouveVille", "Region", "France",
                null, null, false, "Trouvable", null, null, testMembre.getId(), null);
        AdresseResponse created = adresseService.creerAdresse(create);

        AdresseResponse found = adresseService.trouverParId(created.getId());

        assertThat(found).isNotNull();
        assertThat(found.getId()).isEqualTo(created.getId());
        assertThat(found.getVille()).isEqualTo("TrouveVille");
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParId avec UUID inexistant lance NotFoundException")
    void trouverParId_inexistant_throws() {
        assertThatThrownBy(() -> adresseService.trouverParId(UUID.randomUUID()))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("supprimerAdresse: supprime une adresse existante")
    void supprimerAdresse_existant_succeeds() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "BUREAU", "Rue Bureau", null, "22222", "BureauVille", null, "France",
                null, null, false, "Bureau", null, null, testMembre.getId(), null);
        AdresseResponse created = adresseService.creerAdresse(create);

        adresseService.supprimerAdresse(created.getId());

        assertThatThrownBy(() -> adresseService.trouverParId(created.getId()))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("supprimerAdresse avec UUID inexistant lance NotFoundException")
    void supprimerAdresse_inexistant_throws() {
        assertThatThrownBy(() -> adresseService.supprimerAdresse(UUID.randomUUID()))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourAdresse avec UUID inexistant lance NotFoundException")
    void mettreAJourAdresse_inexistant_throws() {
        UpdateAdresseRequest update = new UpdateAdresseRequest(
                null, "Nouvelle Rue", null, null, "NouvVille", null, null, null, null, null, null, null, null, null, null);

        assertThatThrownBy(() -> adresseService.mettreAJourAdresse(UUID.randomUUID(), update))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParOrganisation: retourne les adresses de l'organisation")
    void trouverParOrganisation_returnsList() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "SIEGE", "Siège Social", null, "33333", "SiègeVille", null, "France",
                null, null, false, "Siège", null, testOrganisation.getId(), null, null);
        adresseService.creerAdresse(create);

        List<AdresseResponse> result = adresseService.trouverParOrganisation(testOrganisation.getId());
        assertThat(result).isNotEmpty();
        assertThat(result.stream().anyMatch(a -> "SiègeVille".equals(a.getVille()))).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParOrganisation avec ID inexistant retourne liste vide")
    void trouverParOrganisation_inexistant_returnsEmpty() {
        List<AdresseResponse> result = adresseService.trouverParOrganisation(UUID.randomUUID());
        assertThat(result).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParMembre: retourne les adresses d'un membre")
    void trouverParMembre_returnsList() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "DOMICILE", "Domicile Membre", null, "44444", "MembreVille", null, "France",
                null, null, false, "Domicile", null, null, testMembre.getId(), null);
        adresseService.creerAdresse(create);

        List<AdresseResponse> result = adresseService.trouverParMembre(testMembre.getId());
        assertThat(result).isNotEmpty();
        assertThat(result.stream().anyMatch(a -> "MembreVille".equals(a.getVille()))).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParMembre avec ID inexistant retourne liste vide")
    void trouverParMembre_inexistant_returnsEmpty() {
        List<AdresseResponse> result = adresseService.trouverParMembre(UUID.randomUUID());
        assertThat(result).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverPrincipaleParOrganisation: retourne l'adresse principale")
    void trouverPrincipaleParOrganisation_returnsPrincipale() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "SIEGE", "Siège Principal", null, "55555", "PrincipaleVille", null, "France",
                null, null, true, "Principale", null, testOrganisation.getId(), null, null);
        adresseService.creerAdresse(create);

        AdresseResponse principale = adresseService.trouverPrincipaleParOrganisation(testOrganisation.getId());
        assertThat(principale).isNotNull();
        assertThat(principale.getPrincipale()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverPrincipaleParOrganisation sans adresse principale retourne null")
    void trouverPrincipaleParOrganisation_noMain_returnsNull() {
        AdresseResponse principale = adresseService.trouverPrincipaleParOrganisation(UUID.randomUUID());
        assertThat(principale).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverPrincipaleParMembre: retourne l'adresse principale du membre")
    void trouverPrincipaleParMembre_returnsPrincipale() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                "DOMICILE", "Domicile Principal", null, "66666", "PrincipaleVilleMembre", null, "France",
                null, null, true, "Principale membre", null, null, testMembre.getId(), null);
        adresseService.creerAdresse(create);

        AdresseResponse principale = adresseService.trouverPrincipaleParMembre(testMembre.getId());
        assertThat(principale).isNotNull();
        assertThat(principale.getPrincipale()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverPrincipaleParMembre sans adresse principale retourne null")
    void trouverPrincipaleParMembre_noMain_returnsNull() {
        AdresseResponse principale = adresseService.trouverPrincipaleParMembre(UUID.randomUUID());
        assertThat(principale).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParEvenement avec ID inexistant retourne null")
    void trouverParEvenement_inexistant_returnsNull() {
        AdresseResponse result = adresseService.trouverParEvenement(UUID.randomUUID());
        assertThat(result).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourAdresse avec principale=true désactive les autres")
    void mettreAJourAdresse_principale_desactivesOthers() {
        // Create first address as principale
        CreateAdresseRequest first = new CreateAdresseRequest(
                "DOMICILE", "Première Rue", null, "77777", "PremVille", null, "France",
                null, null, true, "Première", null, null, testMembre.getId(), null);
        AdresseResponse firstCreated = adresseService.creerAdresse(first);

        // Create second address - NOT principale
        CreateAdresseRequest second = new CreateAdresseRequest(
                "BUREAU", "Deuxième Rue", null, "77778", "DeuxVille", null, "France",
                null, null, false, "Deuxième", null, null, testMembre.getId(), null);
        AdresseResponse secondCreated = adresseService.creerAdresse(second);

        // Update second to become principale
        UpdateAdresseRequest update = new UpdateAdresseRequest(
                null, null, null, null, null, null, null, null, null, true, null, null, null, null, null);
        AdresseResponse updatedSecond = adresseService.mettreAJourAdresse(secondCreated.getId(), update);

        assertThat(updatedSecond.getPrincipale()).isTrue();

        // First should now be not principale
        AdresseResponse firstAfter = adresseService.trouverParId(firstCreated.getId());
        assertThat(firstAfter.getPrincipale()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse sans typeAdresse utilise AUTRE par défaut")
    void creerAdresse_noTypeAdresse_usesAUTRE() {
        CreateAdresseRequest create = new CreateAdresseRequest(
                null, "Rue Sans Type", null, "88888", "SansTypeVille", null, "France",
                null, null, false, null, null, null, testMembre.getId(), null);
        AdresseResponse created = adresseService.creerAdresse(create);
        assertThat(created.getTypeAdresse()).isEqualTo("AUTRE");
    }

    @Test
    @TestTransaction
    @DisplayName("desactiverAutresPrincipales via organisation: désactive les anciennes principales")
    void desactiverAutresPrincipales_organisation_works() {
        CreateAdresseRequest first = new CreateAdresseRequest(
                "SIEGE", "Siège 1", null, "99991", "SiègeVille1", null, "France",
                null, null, true, "Siège 1", null, testOrganisation.getId(), null, null);
        AdresseResponse firstCreated = adresseService.creerAdresse(first);

        CreateAdresseRequest second = new CreateAdresseRequest(
                "SIEGE", "Siège 2", null, "99992", "SiègeVille2", null, "France",
                null, null, true, "Siège 2", null, testOrganisation.getId(), null, null);
        AdresseResponse secondCreated = adresseService.creerAdresse(second);

        assertThat(secondCreated.getPrincipale()).isTrue();

        // First should have been deactivated
        AdresseResponse firstAfter = adresseService.trouverParId(firstCreated.getId());
        assertThat(firstAfter.getPrincipale()).isFalse();
    }

    // -------------------------------------------------------------------------
    // Tests complémentaires
    // -------------------------------------------------------------------------

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec organisationId inexistant lance NotFoundException")
    void creerAdresse_organisationIdInexistant_throws() {
        CreateAdresseRequest request = new CreateAdresseRequest(
                "SIEGE", "Rue Test", null, "00000", "Ville", null, "France",
                null, null, false, null, null, UUID.randomUUID(), null, null);

        assertThatThrownBy(() -> adresseService.creerAdresse(request))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec membreId inexistant lance NotFoundException")
    void creerAdresse_membreIdInexistant_throws() {
        CreateAdresseRequest request = new CreateAdresseRequest(
                "DOMICILE", "Rue Test", null, "00000", "Ville", null, "France",
                null, null, false, null, null, null, UUID.randomUUID(), null);

        assertThatThrownBy(() -> adresseService.creerAdresse(request))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec evenementId inexistant lance NotFoundException")
    void creerAdresse_evenementIdInexistant_throws() {
        CreateAdresseRequest request = new CreateAdresseRequest(
                "AUTRE", "Rue Test", null, "00000", "Ville", null, "France",
                null, null, false, null, null, null, null, UUID.randomUUID());

        assertThatThrownBy(() -> adresseService.creerAdresse(request))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec un evenement valide crée l'adresse")
    void creerAdresse_avecEvenement_createsAdresse() {
        // Crée un événement persisté directement
        Evenement evenement = new Evenement();
        evenement.setTitre("Conférence Test " + UUID.randomUUID());
        evenement.setDateDebut(LocalDateTime.now().plusDays(10));
        evenement.setStatut("PLANIFIE");
        evenement.setInscriptionRequise(false);
        evenement.setVisiblePublic(true);
        evenement.setActif(true);
        evenementRepository.persist(evenement);

        CreateAdresseRequest request = new CreateAdresseRequest(
                "AUTRE", "Salle de conf", null, "75001", "Paris", "IDF", "France",
                null, null, true, "Lieu conférence", null, null, null, evenement.getId());

        AdresseResponse response = adresseService.creerAdresse(request);

        assertThat(response).isNotNull();
        assertThat(response.getEvenementId()).isEqualTo(evenement.getId());
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParEvenement retourne l'adresse d'un événement existant")
    void trouverParEvenement_existant_returnsAdresse() {
        // Crée un événement persisté directement
        Evenement evenement = new Evenement();
        evenement.setTitre("Événement Adresse " + UUID.randomUUID());
        evenement.setDateDebut(LocalDateTime.now().plusDays(5));
        evenement.setStatut("PLANIFIE");
        evenement.setInscriptionRequise(false);
        evenement.setVisiblePublic(true);
        evenement.setActif(true);
        evenementRepository.persist(evenement);

        // Crée l'adresse liée à cet événement
        CreateAdresseRequest request = new CreateAdresseRequest(
                "AUTRE", "Lieu Événement", null, "69000", "Lyon", "AURA", "France",
                null, null, true, "Salle", null, null, null, evenement.getId());
        adresseService.creerAdresse(request);

        AdresseResponse result = adresseService.trouverParEvenement(evenement.getId());

        assertThat(result).isNotNull();
        assertThat(result.getEvenementId()).isEqualTo(evenement.getId());
        assertThat(result.getVille()).isEqualTo("Lyon");
    }

    @Test
    @TestTransaction
    @DisplayName("desactiverAutresPrincipales via evenementId: désactive les anciennes principales")
    void desactiverAutresPrincipales_evenement_works() {
        // Crée un événement persisté directement
        Evenement evenement = new Evenement();
        evenement.setTitre("Événement Principal " + UUID.randomUUID());
        evenement.setDateDebut(LocalDateTime.now().plusDays(20));
        evenement.setStatut("PLANIFIE");
        evenement.setInscriptionRequise(false);
        evenement.setVisiblePublic(true);
        evenement.setActif(true);
        evenementRepository.persist(evenement);

        // Première adresse principale pour cet événement
        CreateAdresseRequest first = new CreateAdresseRequest(
                "AUTRE", "Salle A", null, "13000", "Marseille", "PACA", "France",
                null, null, true, "Salle A", null, null, null, evenement.getId());
        AdresseResponse firstCreated = adresseService.creerAdresse(first);

        // Deuxième adresse principale (doit désactiver la première)
        CreateAdresseRequest second = new CreateAdresseRequest(
                "AUTRE", "Salle B", null, "13001", "Marseille", "PACA", "France",
                null, null, true, "Salle B", null, null, null, evenement.getId());
        AdresseResponse secondCreated = adresseService.creerAdresse(second);

        assertThat(secondCreated.getPrincipale()).isTrue();

        // La première doit être désactivée
        AdresseResponse firstAfter = adresseService.trouverParId(firstCreated.getId());
        assertThat(firstAfter.getPrincipale()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("desactiverAutresPrincipales avec tous IDs null — branche return immédiate")
    void desactiverAutresPrincipales_allNullIds_returnsImmediately() {
        // Crée une adresse sans organisationId, membreId, ni evenementId et principale=true.
        // Pour créer une adresse valide sans associations, on utilise principale=false
        // puis on vérifie que creerAdresse(principale=true, aucune asso) ne plante pas.
        // La branche return (ligne 218) est déclenchée quand organisationId=null, membreId=null, evenementId=null.
        CreateAdresseRequest request = new CreateAdresseRequest(
                "DOMICILE", "Rue Sans Asso", null, "00000", "Ville", null, "France",
                null, null, true, null, null,
                null, // organisationId = null
                null, // membreId = null
                null  // evenementId = null
        );

        // creerAdresse appelle desactiverAutresPrincipales(null, null, null) → branche else return
        AdresseResponse response = adresseService.creerAdresse(request);
        assertThat(response).isNotNull();
        assertThat(response.getPrincipale()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec typeAdresse null et principale=false — resolveLibelle avec code=null retourne null")
    void creerAdresse_typeAdresseNull_resolveLibelleReturnsNull() {
        // typeAdresse=null → convertToEntity met "AUTRE" → resolveLibelle("TYPE_ADRESSE", "AUTRE") → code not null
        // Pour couvrir code=null dans resolveLibelle/resolveIcone, on force typeAdresse=null
        // mais convertToEntity remplace null par "AUTRE", donc la seule way to get code=null is via convertToDTO
        // where adresse.getTypeAdresse() is null.
        // This test creates an adresse and sets typeAdresse = null directly via entity for the convertToDTO path.
        // Since we can't easily do that via service, we use the existing coverage path here.
        CreateAdresseRequest request = new CreateAdresseRequest(
                null, "Rue Type Null", null, "11111", "Ville2", null, "France",
                null, null, false, null, null, null, testMembre.getId(), null);

        AdresseResponse response = adresseService.creerAdresse(request);
        assertThat(response).isNotNull();
        // typeAdresse becomes "AUTRE" (from convertToEntity null→"AUTRE")
        assertThat(response.getTypeAdresse()).isEqualTo("AUTRE");
    }

    @Test
    @TestTransaction
    @DisplayName("creerAdresse avec principale=false ne désactive pas les autres")
    void creerAdresse_nonPrincipale_doesNotDesactivateOthers() {
        // Crée une adresse principale
        CreateAdresseRequest first = new CreateAdresseRequest(
                "DOMICILE", "Rue Principale", null, "31000", "Toulouse", null, "France",
                null, null, true, "Principale", null, null, testMembre.getId(), null);
        AdresseResponse firstCreated = adresseService.creerAdresse(first);

        // Crée une seconde adresse non principale
        CreateAdresseRequest second = new CreateAdresseRequest(
                "BUREAU", "Rue Bureau", null, "31001", "Toulouse", null, "France",
                null, null, false, "Bureau", null, null, testMembre.getId(), null);
        adresseService.creerAdresse(second);

        // La première doit toujours être principale
        AdresseResponse firstAfter = adresseService.trouverParId(firstCreated.getId());
        assertThat(firstAfter.getPrincipale()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourAdresse avec principale=true et adresse liée à organisation — couvre branche organisation != null")
    void mettreAJourAdresse_principaleTrue_adresseAvecOrganisation_desactivesOthers() {
        // Première adresse principale pour l'organisation
        CreateAdresseRequest first = new CreateAdresseRequest(
                "SIEGE", "Rue Org 1", null, "10001", "OrgVille1", null, "France",
                null, null, true, "Org1", null, testOrganisation.getId(), null, null);
        AdresseResponse firstCreated = adresseService.creerAdresse(first);

        // Deuxième adresse NON principale pour l'organisation
        CreateAdresseRequest second = new CreateAdresseRequest(
                "BUREAU", "Rue Org 2", null, "10002", "OrgVille2", null, "France",
                null, null, false, "Org2", null, testOrganisation.getId(), null, null);
        AdresseResponse secondCreated = adresseService.creerAdresse(second);

        // Mettre à jour la deuxième en principale → déclenche desactiverAutresPrincipales via org
        UpdateAdresseRequest update = new UpdateAdresseRequest(
                null, null, null, null, null, null, null, null, null, true, null, null, null, null, null);
        AdresseResponse updatedSecond = adresseService.mettreAJourAdresse(secondCreated.getId(), update);

        assertThat(updatedSecond.getPrincipale()).isTrue();

        // Première adresse doit être désactivée
        AdresseResponse firstAfter = adresseService.trouverParId(firstCreated.getId());
        assertThat(firstAfter.getPrincipale()).isFalse();
    }

    // -------------------------------------------------------------------------
    // Tests cas limites : convertToDTO(null), convertToEntity(null), mettreAJourAdresse avec evenement
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("convertToDTO avec adresse null retourne null")
    void convertToDTO_nullAdresse_returnsNull() throws Exception {
        java.lang.reflect.Method method = AdresseService.class.getDeclaredMethod(
                "convertToDTO", dev.lions.unionflow.server.entity.Adresse.class);
        method.setAccessible(true);
        Object result = method.invoke(adresseService, (Object) null);
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("convertToEntity avec request null retourne null (branche ligne 270 via réflexion)")
    void convertToEntity_nullRequest_returnsNull() throws Exception {
        java.lang.reflect.Method method = AdresseService.class.getDeclaredMethod(
                "convertToEntity", dev.lions.unionflow.server.api.dto.adresse.request.CreateAdresseRequest.class);
        method.setAccessible(true);
        Object result = method.invoke(adresseService, (Object) null);
        assertThat(result).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourAdresse avec principale=true et adresse liée à événement — couvre branche evenement != null")
    void mettreAJourAdresse_principaleTrue_adresseAvecEvenement_desactivesOthers() {
        // Créer un événement persisté
        Evenement evenement = new Evenement();
        evenement.setTitre("Événement MàJ Principal " + UUID.randomUUID());
        evenement.setDateDebut(LocalDateTime.now().plusDays(30));
        evenement.setStatut("PLANIFIE");
        evenement.setInscriptionRequise(false);
        evenement.setVisiblePublic(true);
        evenement.setActif(true);
        evenementRepository.persist(evenement);

        // Créer une première adresse principale pour l'événement
        CreateAdresseRequest first = new CreateAdresseRequest(
                "AUTRE", "Salle Première", null, "75001", "Paris", "IDF", "France",
                null, null, true, "Principale initiale", null, null, null, evenement.getId());
        AdresseResponse firstCreated = adresseService.creerAdresse(first);

        // Créer une deuxième adresse NON principale pour l'événement
        CreateAdresseRequest second = new CreateAdresseRequest(
                "AUTRE", "Salle Seconde", null, "75002", "Paris", "IDF", "France",
                null, null, false, "Seconde", null, null, null, evenement.getId());
        AdresseResponse secondCreated = adresseService.creerAdresse(second);

        // Mettre à jour la seconde en principale → déclenche desactiverAutresPrincipales(null, null, evenementId)
        UpdateAdresseRequest update = new UpdateAdresseRequest(
                null, null, null, null, null, null, null, null, null, true, null, null, null, null, null);
        AdresseResponse updatedSecond = adresseService.mettreAJourAdresse(secondCreated.getId(), update);

        assertThat(updatedSecond.getPrincipale()).isTrue();

        // La première adresse doit être désactivée
        AdresseResponse firstAfter = adresseService.trouverParId(firstCreated.getId());
        assertThat(firstAfter.getPrincipale()).isFalse();
    }

    @Test
    @DisplayName("resolveLibelle avec code null retourne null (via réflexion)")
    void resolveLibelle_codeNull_returnsNull() throws Exception {
        java.lang.reflect.Method method = AdresseService.class.getDeclaredMethod(
                "resolveLibelle", String.class, String.class);
        method.setAccessible(true);
        Object result = method.invoke(adresseService, "TYPE_ADRESSE", null);
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("resolveIcone avec code null retourne null (via réflexion)")
    void resolveIcone_codeNull_returnsNull() throws Exception {
        java.lang.reflect.Method method = AdresseService.class.getDeclaredMethod(
                "resolveIcone", String.class, String.class);
        method.setAccessible(true);
        Object result = method.invoke(adresseService, "TYPE_ADRESSE", null);
        assertThat(result).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourAdresse avec tous les champs — couvre toutes les branches de updateFromDTO")
    void mettreAJourAdresse_allFieldsNonNull_coversAllUpdateFromDtoBranches() {
        // Créer une adresse initiale minimale
        CreateAdresseRequest create = new CreateAdresseRequest(
                "DOMICILE", "Rue Initiale", null, "00000", "VilleInit", null, "PaysInit",
                null, null, false, "Libellé Init", null, null, testMembre.getId(), null);
        AdresseResponse created = adresseService.creerAdresse(create);

        // Tous les champs non-null → couvre typeAdresse, complementAdresse, region,
        // pays, latitude, longitude dans updateFromDTO (branches manquantes)
        UpdateAdresseRequest update = new UpdateAdresseRequest(
                "BUREAU",           // typeAdresse
                "Nouvelle Rue",     // adresse
                "Bât A",            // complementAdresse
                "75001",            // codePostal
                "Paris",            // ville
                "Île-de-France",    // region
                "France",           // pays
                BigDecimal.valueOf(48.8566),  // latitude
                BigDecimal.valueOf(2.3522),   // longitude
                false,              // principale
                "Nouveau Libellé",  // libelle
                "Nouvelles notes",  // notes
                null,               // organisationId
                null,               // membreId
                null                // evenementId
        );

        AdresseResponse result = adresseService.mettreAJourAdresse(created.getId(), update);

        assertThat(result.getTypeAdresse()).isEqualTo("BUREAU");
        assertThat(result.getComplementAdresse()).isEqualTo("Bât A");
        assertThat(result.getRegion()).isEqualTo("Île-de-France");
        assertThat(result.getPays()).isEqualTo("France");
        assertThat(result.getLatitude()).isEqualByComparingTo(BigDecimal.valueOf(48.8566));
        assertThat(result.getLongitude()).isEqualByComparingTo(BigDecimal.valueOf(2.3522));
    }
}
