package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.notification.request.CreateNotificationRequest;
import dev.lions.unionflow.server.entity.Membre;
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.arc.ClientProxy;
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.Mailer;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests pour {@link NotificationService#envoyerEmail} — branche catch (lignes 403-408).
 *
 * <p>Utilise la réflexion pour remplacer le {@code Mailer} injecté dans le service réel
 * par un mock qui lève une exception, couvrant ainsi le bloc catch de {@code envoyerEmail}
 * sans nécessiter de redémarrage Quarkus ({@code @TestProfile}).
 *
 * <p>Note : {@code Mailer} est un bean {@code @Singleton} et ne peut pas être remplacé via
 * {@code @InjectMock} (qui ne fonctionne qu'avec les scopes CDI normaux). La réflexion sur
 * le bean réel (via {@code ClientProxy.arc_contextualInstance()}) contourne cette limitation.
 */
@QuarkusTest
@DisplayName("NotificationService.envoyerEmail — catch block mailer")
class NotificationServiceEmailCatchTest {

    @Inject
    NotificationService notificationService;

    @InjectMock
    KeycloakService keycloakService;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    NotificationRepository notificationRepository;

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    TemplateNotificationRepository templateNotificationRepository;

    private static final UUID MEMBRE_ID = UUID.fromString("aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb");

    private Mailer originalMailer;

    @BeforeEach
    void setUp() throws Exception {
        Membre membre = new Membre();
        membre.setId(MEMBRE_ID);
        membre.setNom("Catch");
        membre.setPrenom("Test");
        membre.setEmail("catch@test.com");
        membre.setNumeroMembre("CATCH-001");
        membre.setActif(true);

        when(membreRepository.findByIdOptional(MEMBRE_ID)).thenReturn(Optional.of(membre));
        when(keycloakService.getCurrentUserEmail()).thenReturn("system@test.com");

        // Replace the real Mailer with a mock that throws → covers catch block lines 403-408
        NotificationService realService =
                (NotificationService) ((ClientProxy) notificationService).arc_contextualInstance();
        Field mailerField = findField(realService.getClass(), "mailer");
        mailerField.setAccessible(true);
        originalMailer = (Mailer) mailerField.get(realService);

        Mailer failingMailer = mock(Mailer.class);
        doThrow(new RuntimeException("SMTP connection refused")).when(failingMailer).send(any(Mail.class));
        mailerField.set(realService, failingMailer);
    }

    @AfterEach
    void restoreMailer() throws Exception {
        if (originalMailer != null) {
            NotificationService realService =
                    (NotificationService) ((ClientProxy) notificationService).arc_contextualInstance();
            Field mailerField = findField(realService.getClass(), "mailer");
            mailerField.setAccessible(true);
            mailerField.set(realService, originalMailer);
        }
    }

    private Field findField(Class<?> clazz, String name) throws NoSuchFieldException {
        while (clazz != null) {
            try {
                return clazz.getDeclaredField(name);
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        throw new NoSuchFieldException(name);
    }

    @Test
    @DisplayName("creerNotification EMAIL avec Mailer qui échoue — couvre catch block envoyerEmail (lignes 403-408)")
    void creerNotification_smtpFails_coversEnvoyerEmailCatchBlock() {
        CreateNotificationRequest request = CreateNotificationRequest.builder()
                .typeNotification("EMAIL")
                .membreId(MEMBRE_ID)
                .sujet("Test sujet")
                .corps("Test corps")
                .build();

        // Le mailer mocké lève une exception → catch block lignes 403-408 est couvert
        // creerNotification capture aussi l'éventuelle exception → pas de throw
        assertThatCode(() -> notificationService.creerNotification(request)).doesNotThrowAnyException();
    }
}
