package dev.lions.unionflow.server.service;

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

import dev.lions.unionflow.server.api.dto.notification.request.CreateNotificationRequest;
import dev.lions.unionflow.server.api.dto.notification.request.CreateTemplateNotificationRequest;
import dev.lions.unionflow.server.api.dto.notification.response.NotificationResponse;
import dev.lions.unionflow.server.api.dto.notification.response.TemplateNotificationResponse;

import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Notification;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.TemplateNotification;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.NotificationRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.repository.TemplateNotificationRepository;
import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.*;

/**
 * Tests unitaires pour NotificationService
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-13
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class NotificationServiceTest {

    @Inject
    NotificationService notificationService;
    @Inject
    NotificationRepository notificationRepository;
    @Inject
    MembreRepository membreRepository;
    @Inject
    OrganisationRepository organisationRepository;
    @Inject
    TemplateNotificationRepository templateNotificationRepository;

    private Membre testMembre;
    private Notification testNotification;

    @BeforeEach
    @Transactional
    void setupTestData() {
        // Créer un membre de test
        testMembre = Membre.builder()
                .nom("Test")
                .prenom("Notification")
                .email("test.notification." + System.currentTimeMillis() + "@unionflow.dev")
                .numeroMembre("NOTIF-" + System.currentTimeMillis())
                .dateNaissance(java.time.LocalDate.of(1990, 1, 1))
                .build();
        testMembre.setActif(true);
        membreRepository.persist(testMembre);

        // Créer une notification de test
        testNotification = Notification.builder()
                .membre(testMembre)
                .typeNotification("IN_APP")
                .priorite("NORMALE")
                .statut("NON_LUE")
                .sujet("Test Notification")
                .corps("Ceci est une notification de test")
                .build();
        testNotification.setActif(true);
        notificationRepository.persist(testNotification);
    }

    @AfterEach
    @Transactional
    void cleanupTestData() {
        // Supprimer toutes les notifications du membre de test pour éviter les
        // violations d'intégrité
        if (testMembre != null && testMembre.getId() != null) {
            notificationRepository.findByMembreId(testMembre.getId())
                    .forEach(n -> notificationRepository.delete(n));
        }

        // Supprimer le membre de test
        if (testMembre != null && testMembre.getId() != null) {
            Membre membreToDelete = membreRepository.findById(testMembre.getId());
            if (membreToDelete != null) {
                membreRepository.delete(membreToDelete);
            }
        }
    }

    @Test
    @Order(1)
    @Transactional
    @DisplayName("Devrait créer une nouvelle notification")
    void testCreerNotification() {
        // Given
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .typeNotification("IN_APP")
                .priorite("HAUTE")
                .sujet("Nouvelle Notification")
                .corps("Contenu de la notification")
                .build();

        // When
        NotificationResponse created = notificationService.creerNotification(request);

        // Then
        assertThat(created).isNotNull();
        assertThat(created.getId()).isNotNull();
        assertThat(created.getSujet()).isEqualTo("Nouvelle Notification");
        // Status should be EN_ATTENTE because we used IN_APP type (not immediate EMAIL
        // send)
        assertThat(created.getStatut()).isEqualTo("EN_ATTENTE");
        assertThat(created.getPriorite()).isEqualTo("HAUTE");

        // Cleanup
        Notification createdEntity = notificationRepository.findById(created.getId());
        if (createdEntity != null) {
            notificationRepository.delete(createdEntity);
        }
    }

    @Test
    @Order(2)
    @DisplayName("Devrait marquer une notification comme lue")
    void testMarquerCommeLue() {
        // Given
        UUID notificationId = testNotification.getId();

        // When
        NotificationResponse updated = notificationService.marquerCommeLue(notificationId);

        // Then
        assertThat(updated).isNotNull();
        assertThat(updated.getStatut()).isEqualTo("LUE");
        assertThat(updated.getDateLecture()).isNotNull();
    }

    @Test
    @Order(3)
    @DisplayName("Devrait trouver une notification par son ID")
    void testTrouverNotificationParId() {
        // Given
        UUID notificationId = testNotification.getId();

        // When
        NotificationResponse found = notificationService.trouverNotificationParId(notificationId);

        // Then
        assertThat(found).isNotNull();
        assertThat(found.getId()).isEqualTo(notificationId);
        assertThat(found.getSujet()).isEqualTo("Test Notification");
    }

    @Test
    @Order(4)
    @DisplayName("Devrait lister les notifications d'un membre")
    void testListerNotificationsParMembre() {
        // Given
        UUID membreId = testMembre.getId();

        // When
        List<NotificationResponse> notifications = notificationService.listerNotificationsParMembre(membreId);

        // Then
        assertThat(notifications).isNotNull();
        assertThat(notifications).isNotEmpty();
        assertThat(notifications).anyMatch(n -> n.getId().equals(testNotification.getId()));
    }

    @Test
    @Order(5)
    @DisplayName("Devrait lister les notifications non lues d'un membre")
    void testListerNotificationsNonLuesParMembre() {
        // Given
        UUID membreId = testMembre.getId();

        // When
        List<NotificationResponse> notifications = notificationService.listerNotificationsNonLuesParMembre(membreId);

        // Then
        assertThat(notifications).isNotNull();
        assertThat(notifications).isNotEmpty();
        assertThat(notifications)
                .allMatch(n -> !"LUE".equals(n.getStatut()));
    }

    @Test
    @Order(6)
    @DisplayName("Devrait envoyer des notifications groupées")
    @Transactional
    void testEnvoyerNotificationsGroupees() {
        // Given
        List<UUID> membreIds = List.of(testMembre.getId());
        String sujet = "Notification Groupée";
        String corps = "Message groupé de test";
        List<String> canaux = List.of("IN_APP");

        // When
        int notificationsCreees = notificationService.envoyerNotificationsGroupees(membreIds, sujet, corps, canaux);

        // Then
        assertThat(notificationsCreees).isEqualTo(1);

        // Vérifier que la notification a été créée
        List<NotificationResponse> notifications = notificationService.listerNotificationsParMembre(testMembre.getId());
        assertThat(notifications)
                .anyMatch(n -> n.getSujet().equals("Notification Groupée"));

        // Cleanup
        notifications.stream()
                .filter(n -> n.getSujet().equals("Notification Groupée"))
                .forEach(
                        n -> {
                            Notification toDelete = notificationRepository.findById(n.getId());
                            if (toDelete != null) {
                                notificationRepository.delete(toDelete);
                            }
                        });
    }

    @Test
    @Order(7)
    @DisplayName("Devrait lever une exception si la notification n'existe pas")
    void testTrouverNotificationInexistante() {
        // Given
        UUID notificationInexistante = UUID.randomUUID();

        // When/Then
        assertThatThrownBy(() -> notificationService.trouverNotificationParId(notificationInexistante))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class)
                .hasMessageContaining("Notification non trouvée");
    }

    // ===== creerTemplate =====

    @Test
    @Order(8)
    @Transactional
    @DisplayName("Devrait créer un nouveau template de notification")
    void testCreerTemplate() {
        // Given
        String code = "TEMPLATE-TEST-" + System.currentTimeMillis();
        CreateTemplateNotificationRequest request = CreateTemplateNotificationRequest.builder()
                .code(code)
                .sujet("Sujet template test")
                .corpsTexte("Corps texte du template")
                .corpsHtml("<p>Corps HTML</p>")
                .variablesDisponibles("{\"nom\": \"string\"}")
                .canauxSupportes("EMAIL,IN_APP")
                .langue("fr")
                .description("Template de test")
                .build();

        // When
        TemplateNotificationResponse created = notificationService.creerTemplate(request);

        // Then
        assertThat(created).isNotNull();
        assertThat(created.getId()).isNotNull();
        assertThat(created.getCode()).isEqualTo(code);
        assertThat(created.getSujet()).isEqualTo("Sujet template test");
        assertThat(created.getLangue()).isEqualTo("fr");

        // Cleanup
        TemplateNotification entity = templateNotificationRepository.findById(created.getId());
        if (entity != null) {
            templateNotificationRepository.delete(entity);
        }
    }

    @Test
    @Order(9)
    @Transactional
    @DisplayName("Devrait lever une exception si le code du template existe déjà")
    void testCreerTemplate_codeDupliqué_throwsException() {
        // Given: créer un premier template
        String code = "TEMPLATE-DUP-" + System.currentTimeMillis();
        TemplateNotification existing = TemplateNotification.builder()
                .code(code)
                .sujet("Sujet existant")
                .langue("fr")
                .build();
        existing.setActif(true);
        templateNotificationRepository.persist(existing);

        CreateTemplateNotificationRequest request = CreateTemplateNotificationRequest.builder()
                .code(code)
                .sujet("Autre sujet")
                .build();

        // When/Then
        assertThatThrownBy(() -> notificationService.creerTemplate(request))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("code existe déjà");

        // Cleanup
        templateNotificationRepository.delete(existing);
    }

    @Test
    @Order(10)
    @Transactional
    @DisplayName("Devrait créer un template avec langue par défaut 'fr' quand langue null")
    void testCreerTemplate_langueNull_utiliseDefautFr() {
        String code = "TEMPLATE-LANG-" + System.currentTimeMillis();
        CreateTemplateNotificationRequest request = CreateTemplateNotificationRequest.builder()
                .code(code)
                .sujet("Test langue null")
                .langue(null)  // null → doit utiliser "fr"
                .build();

        TemplateNotificationResponse created = notificationService.creerTemplate(request);
        assertThat(created.getLangue()).isEqualTo("fr");

        // Cleanup
        TemplateNotification entity = templateNotificationRepository.findById(created.getId());
        if (entity != null) {
            templateNotificationRepository.delete(entity);
        }
    }

    // ===== creerNotification — branche EMAIL =====

    @Test
    @Order(11)
    @Transactional
    @DisplayName("Devrait créer une notification de type EMAIL (envoi immédiat tenté)")
    void testCreerNotification_typeEmail_envoiImmediatTente() {
        // Given: notification EMAIL — l'envoi email peut échouer (maildev absent)
        // mais la notification doit quand même être créée
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .typeNotification("EMAIL")
                .priorite("HAUTE")
                .sujet("Email Test")
                .corps("Corps du mail de test")
                .build();

        // When: ne doit pas lancer d'exception même si l'email échoue
        NotificationResponse created = notificationService.creerNotification(request);

        // Then
        assertThat(created).isNotNull();
        assertThat(created.getId()).isNotNull();
        assertThat(created.getSujet()).isEqualTo("Email Test");

        // Cleanup
        Notification entity = notificationRepository.findById(created.getId());
        if (entity != null) {
            notificationRepository.delete(entity);
        }
    }

    // ===== listerNotificationsEnAttenteEnvoi =====

    @Test
    @Order(12)
    @DisplayName("Devrait lister les notifications en attente d'envoi")
    void testListerNotificationsEnAttenteEnvoi() {
        // When
        List<NotificationResponse> notifications = notificationService.listerNotificationsEnAttenteEnvoi();

        // Then
        assertThat(notifications).isNotNull();
        // testNotification est NON_LUE (pas EN_ATTENTE), donc peut ne pas apparaître
        // Mais la liste ne doit pas être null
    }

    // ===== envoyerNotificationsGroupees — branches =====

    @Test
    @Order(13)
    @DisplayName("envoyerNotificationsGroupees liste null → IllegalArgumentException")
    void testEnvoyerNotificationsGroupees_null_throws() {
        assertThatThrownBy(() -> notificationService.envoyerNotificationsGroupees(null, "Sujet", "Corps", List.of("IN_APP")))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("vide");
    }

    @Test
    @Order(14)
    @DisplayName("envoyerNotificationsGroupees liste vide → IllegalArgumentException")
    void testEnvoyerNotificationsGroupees_listeVide_throws() {
        assertThatThrownBy(() -> notificationService.envoyerNotificationsGroupees(List.of(), "Sujet", "Corps", List.of("IN_APP")))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("vide");
    }

    @Test
    @Order(15)
    @Transactional
    @DisplayName("envoyerNotificationsGroupees avec membre inexistant → 0 notifications créées (exception catchée)")
    void testEnvoyerNotificationsGroupees_membreInexistant_zeroCreees() {
        int result = notificationService.envoyerNotificationsGroupees(
                List.of(UUID.randomUUID()), "Sujet", "Corps", List.of("IN_APP"));
        assertThat(result).isEqualTo(0);
    }

    @Test
    @Order(16)
    @Transactional
    @DisplayName("envoyerNotificationsGroupees avec canaux null → utilise IN_APP par défaut")
    void testEnvoyerNotificationsGroupees_canauxNull_utiliseInAppParDefaut() {
        int result = notificationService.envoyerNotificationsGroupees(
                List.of(testMembre.getId()), "Sujet défaut", "Corps", null);
        assertThat(result).isEqualTo(1);

        // Cleanup notifications créées
        notificationRepository.findByMembreId(testMembre.getId()).stream()
                .filter(n -> "Sujet défaut".equals(n.getSujet()))
                .forEach(notificationRepository::delete);
    }

    @Test
    @Order(17)
    @Transactional
    @DisplayName("envoyerNotificationsGroupees avec canal EMAIL → envoi email tenté")
    void testEnvoyerNotificationsGroupees_canalEmail_envoiTente() {
        // EMAIL envoi peut échouer (maildev absent) mais la notification est créée
        int result = notificationService.envoyerNotificationsGroupees(
                List.of(testMembre.getId()), "Email Groupé", "Corps email", List.of("EMAIL"));
        // La notification peut être créée (1) même si l'email échoue
        assertThat(result).isGreaterThanOrEqualTo(0);

        // Cleanup
        notificationRepository.findByMembreId(testMembre.getId()).stream()
                .filter(n -> "Email Groupé".equals(n.getSujet()))
                .forEach(notificationRepository::delete);
    }

    // ===== creerNotification — avec organisationId =====

    @Test
    @Order(18)
    @Transactional
    @DisplayName("creerNotification avec organisationId valide → organisation associée")
    void testCreerNotification_avecOrganisation_organisationAssociee() {
        // Given
        Organisation org = new Organisation();
        org.setNom("Org Notification Test " + System.currentTimeMillis());
        org.setEmail("org-notif-" + System.currentTimeMillis() + "@test.com");
        org.setTypeOrganisation("ASSOCIATION");
        org.setStatut("ACTIVE");
        org.setActif(true);
        organisationRepository.persist(org);

        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .organisationId(org.getId())
                .typeNotification("IN_APP")
                .priorite("NORMALE")
                .sujet("Notification avec org")
                .corps("Corps")
                .build();

        NotificationResponse created = notificationService.creerNotification(request);
        assertThat(created).isNotNull();
        assertThat(created.getOrganisationId()).isEqualTo(org.getId());

        // Cleanup
        Notification entity = notificationRepository.findById(created.getId());
        if (entity != null) {
            notificationRepository.delete(entity);
        }
        organisationRepository.delete(org);
    }

    @Test
    @Order(19)
    @DisplayName("creerNotification avec organisationId inexistant → NotFoundException")
    void testCreerNotification_organisationInexistante_throws() {
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .organisationId(UUID.randomUUID())
                .typeNotification("IN_APP")
                .sujet("Test org inexistante")
                .corps("Corps")
                .build();

        assertThatThrownBy(() -> notificationService.creerNotification(request))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class)
                .hasMessageContaining("Organisation non trouvée");
    }

    @Test
    @Order(20)
    @DisplayName("creerNotification avec membreId inexistant → NotFoundException")
    void testCreerNotification_membreInexistant_throws() {
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(UUID.randomUUID())
                .typeNotification("IN_APP")
                .sujet("Test membre inexistant")
                .corps("Corps")
                .build();

        assertThatThrownBy(() -> notificationService.creerNotification(request))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class)
                .hasMessageContaining("Membre non trouvé");
    }

    @Test
    @Order(21)
    @Transactional
    @DisplayName("creerNotification avec templateId valide → template associé")
    void testCreerNotification_avecTemplate_templateAssocié() {
        // Given: créer un template
        String code = "TPL-NOTIF-" + System.currentTimeMillis();
        TemplateNotification template = TemplateNotification.builder()
                .code(code)
                .sujet("Template sujet")
                .langue("fr")
                .build();
        template.setActif(true);
        templateNotificationRepository.persist(template);

        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .templateId(template.getId())
                .typeNotification("IN_APP")
                .sujet("Notification avec template")
                .corps("Corps")
                .build();

        NotificationResponse created = notificationService.creerNotification(request);
        assertThat(created).isNotNull();
        assertThat(created.getTemplateId()).isEqualTo(template.getId());

        // Cleanup
        Notification entity = notificationRepository.findById(created.getId());
        if (entity != null) {
            notificationRepository.delete(entity);
        }
        templateNotificationRepository.delete(template);
    }

    @Test
    @Order(22)
    @DisplayName("creerNotification avec templateId inexistant → NotFoundException")
    void testCreerNotification_templateInexistant_throws() {
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .templateId(UUID.randomUUID())
                .typeNotification("IN_APP")
                .sujet("Test template inexistant")
                .corps("Corps")
                .build();

        assertThatThrownBy(() -> notificationService.creerNotification(request))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class)
                .hasMessageContaining("Template non trouvé");
    }

    @Test
    @Order(23)
    @Transactional
    @DisplayName("creerNotification avec priorite null → priorite 'NORMALE' par défaut")
    void testCreerNotification_prioriteNull_utiliseNormaleParDefaut() {
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .typeNotification("IN_APP")
                .priorite(null)
                .sujet("Test priorite null")
                .corps("Corps")
                .build();

        NotificationResponse created = notificationService.creerNotification(request);
        assertThat(created.getPriorite()).isEqualTo("NORMALE");

        // Cleanup
        Notification entity = notificationRepository.findById(created.getId());
        if (entity != null) {
            notificationRepository.delete(entity);
        }
    }

    @Test
    @Order(24)
    @Transactional
    @DisplayName("creerNotification avec dateEnvoiPrevue non null → date conservée")
    void testCreerNotification_avecDateEnvoiPrevue_dateConservee() {
        LocalDateTime datePrevue = LocalDateTime.now().plusHours(2);
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .typeNotification("IN_APP")
                .sujet("Test date envoi")
                .corps("Corps")
                .dateEnvoiPrevue(datePrevue)
                .build();

        NotificationResponse created = notificationService.creerNotification(request);
        assertThat(created.getDateEnvoiPrevue()).isNotNull();

        // Cleanup
        Notification entity = notificationRepository.findById(created.getId());
        if (entity != null) {
            notificationRepository.delete(entity);
        }
    }

    @Test
    @Order(25)
    @TestTransaction
    @DisplayName("convertToDTO notification avec organisation → organisationId mappé")
    void testConvertToDTO_avecOrganisation_organisationIdMappe() {
        Organisation org = new Organisation();
        org.setNom("Org DTO Test " + System.currentTimeMillis());
        org.setEmail("org-dto-" + System.currentTimeMillis() + "@test.com");
        org.setTypeOrganisation("CLUB");
        org.setStatut("ACTIVE");
        org.setActif(true);
        organisationRepository.persist(org);

        // Créer une nouvelle notification avec org pour ce test (éviter conflict de version)
        Notification notifAvecOrg = Notification.builder()
                .membre(testNotification.getMembre())
                .sujet("Test DTO Org")
                .corps("Message DTO Org")
                .typeNotification("IN_APP")
                .organisation(org)
                .build();
        notifAvecOrg.setActif(true);
        notifAvecOrg.setDateCreation(java.time.LocalDateTime.now());
        notificationRepository.persist(notifAvecOrg);

        NotificationResponse dto = notificationService.trouverNotificationParId(notifAvecOrg.getId());
        assertThat(dto.getOrganisationId()).isEqualTo(org.getId());
    }

    @Test
    @Order(26)
    @Transactional
    @DisplayName("marquerCommeLue notification inexistante → NotFoundException")
    void testMarquerCommeLue_inexistant_throws() {
        assertThatThrownBy(() -> notificationService.marquerCommeLue(UUID.randomUUID()))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class)
                .hasMessageContaining("Notification non trouvée");
    }

    @Test
    @Order(27)
    @Transactional
    @DisplayName("envoyerNotificationsGroupees avec plusieurs canaux → plusieurs notifications créées")
    void testEnvoyerNotificationsGroupees_plusieurCanaux_plusieursCreees() {
        int result = notificationService.envoyerNotificationsGroupees(
                List.of(testMembre.getId()), "Multi canal", "Corps", List.of("IN_APP", "SMS"));
        assertThat(result).isEqualTo(2);

        // Cleanup
        notificationRepository.findByMembreId(testMembre.getId()).stream()
                .filter(n -> "Multi canal".equals(n.getSujet()))
                .forEach(notificationRepository::delete);
    }

    @Test
    @Order(28)
    @Transactional
    @DisplayName("creerNotification IN_APP avec priorite non null → priorite conservée")
    void testCreerNotification_prioriteNonNull_prioriteConservee() {
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .membreId(testMembre.getId())
                .typeNotification("IN_APP")
                .priorite("URGENTE")
                .sujet("Test priorite non null")
                .corps("Corps")
                .build();

        NotificationResponse created = notificationService.creerNotification(request);
        assertThat(created.getPriorite()).isEqualTo("URGENTE");

        // Cleanup
        Notification entity = notificationRepository.findById(created.getId());
        if (entity != null) {
            notificationRepository.delete(entity);
        }
    }

    @Test
    @Order(29)
    @Transactional
    @DisplayName("envoyerNotificationsGroupees EMAIL avec membre ayant email valide → envoi tenté")
    void testEnvoyerNotificationsGroupees_emailAvecMembreAvecEmail_envoiTente() {
        // testMembre a un email valide → envoyerEmail est appelé
        // L'email peut échouer (mailer absent) mais le compteur est incrémenté
        int result = notificationService.envoyerNotificationsGroupees(
                List.of(testMembre.getId()), "Email Groupé Valide", "Corps email", List.of("EMAIL"));
        // La notification est créée même si l'email échoue
        assertThat(result).isGreaterThanOrEqualTo(0);

        // Cleanup
        notificationRepository.findByMembreId(testMembre.getId()).stream()
                .filter(n -> "Email Groupé Valide".equals(n.getSujet()))
                .forEach(notificationRepository::delete);
    }

    // =========================================================================
    // Null guards des méthodes privées — via réflexion
    // =========================================================================

    @Test
    @DisplayName("convertToDTO(TemplateNotification null) retourne null via réflexion")
    void testConvertToDTO_templateNotificationNull_returnsNull() throws Exception {
        Method method = NotificationService.class.getDeclaredMethod("convertToDTO", TemplateNotification.class);
        method.setAccessible(true);
        Object result = method.invoke(notificationService, (TemplateNotification) null);
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("convertToEntity(CreateTemplateNotificationRequest null) retourne null via réflexion")
    void testConvertToEntity_templateRequestNull_returnsNull() throws Exception {
        Method method = NotificationService.class.getDeclaredMethod("convertToEntity", CreateTemplateNotificationRequest.class);
        method.setAccessible(true);
        Object result = method.invoke(notificationService, (CreateTemplateNotificationRequest) null);
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("convertToDTO(Notification null) retourne null via réflexion")
    void testConvertToDTO_notificationNull_returnsNull() throws Exception {
        Method method = NotificationService.class.getDeclaredMethod("convertToDTO", Notification.class);
        method.setAccessible(true);
        Object result = method.invoke(notificationService, (Notification) null);
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("convertToEntity(CreateNotificationRequest null) retourne null via réflexion")
    void testConvertToEntity_notificationRequestNull_returnsNull() throws Exception {
        Method method = NotificationService.class.getDeclaredMethod("convertToEntity", CreateNotificationRequest.class);
        method.setAccessible(true);
        Object result = method.invoke(notificationService, (CreateNotificationRequest) null);
        assertThat(result).isNull();
    }
}
