package dev.lions.unionflow.server.entity.listener;

import dev.lions.unionflow.server.entity.Adresse;
import dev.lions.unionflow.server.entity.BaseEntity;
import dev.lions.unionflow.server.service.KeycloakService;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.InstanceHandle;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

/**
 * Tests unitaires pour AuditEntityListener (avantCreation, avantModification, toutes les branches).
 */
@DisplayName("AuditEntityListener")
class AuditEntityListenerTest {

    private final AuditEntityListener listener = new AuditEntityListener();

    @Test
    @DisplayName("avantCreation: renseigne creePar quand null")
    void avantCreation_creeParNull_renseigneAvecUtilisateurCourant() {
        BaseEntity entity = new Adresse();
        entity.setCreePar(null);

        listener.avantCreation(entity);

        assertThat(entity.getCreePar()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("avantCreation: renseigne creePar quand blank")
    void avantCreation_creeParBlank_renseigneAvecUtilisateurCourant() {
        BaseEntity entity = new Adresse();
        entity.setCreePar("   ");

        listener.avantCreation(entity);

        assertThat(entity.getCreePar()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("avantCreation: ne modifie pas creePar quand déjà renseigné")
    void avantCreation_creeParDejaRenseigne_neModifiePas() {
        BaseEntity entity = new Adresse();
        entity.setCreePar("deja@test.com");

        listener.avantCreation(entity);

        assertThat(entity.getCreePar()).isEqualTo("deja@test.com");
    }

    @Test
    @DisplayName("avantCreation: ne modifie pas creePar quand chaîne non vide")
    void avantCreation_creeParNonVide_neModifiePas() {
        BaseEntity entity = new Adresse();
        entity.setCreePar("x");

        listener.avantCreation(entity);

        assertThat(entity.getCreePar()).isEqualTo("x");
    }

    @Test
    @DisplayName("avantModification: renseigne toujours modifiePar")
    void avantModification_renseigneModifiePar() {
        BaseEntity entity = new Adresse();
        entity.setModifiePar(null);

        listener.avantModification(entity);

        assertThat(entity.getModifiePar()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("avantModification: écrase modifiePar existant")
    void avantModification_ecraseModifieParExistant() {
        BaseEntity entity = new Adresse();
        entity.setModifiePar("ancien@test.com");

        listener.avantModification(entity);

        assertThat(entity.getModifiePar()).isNotNull();
    }

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("avantCreation: retourne l'email quand KeycloakService authentifié et email non vide")
    void avantCreation_authenticated_withEmail_setsEmail() {
        ArcContainer mockContainer = mock(ArcContainer.class);
        InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
        KeycloakService mockService = mock(KeycloakService.class);

        when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
        when(mockHandle.get()).thenReturn(mockService);
        when(mockService.isAuthenticated()).thenReturn(true);
        when(mockService.getCurrentUserEmail()).thenReturn("user@example.com");

        try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
            arcMock.when(Arc::container).thenReturn(mockContainer);

            BaseEntity entity = new Adresse();
            entity.setCreePar(null);
            new AuditEntityListener().avantCreation(entity);

            assertThat(entity.getCreePar()).isEqualTo("user@example.com");
        }
    }

    // -----------------------------------------------------------------------
    // Branches manquantes dans obtenirUtilisateurCourant()
    // -----------------------------------------------------------------------

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("obtenirUtilisateurCourant: keycloakService null → retourne 'system' (L92 branche keycloakService==null)")
    void avantCreation_keycloakServiceNull_returnsSystem() {
        ArcContainer mockContainer = mock(ArcContainer.class);
        InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);

        when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
        // get() retourne null → L92 condition (keycloakService != null) = false → retourne "system"
        when(mockHandle.get()).thenReturn(null);

        try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
            arcMock.when(Arc::container).thenReturn(mockContainer);

            BaseEntity entity = new Adresse();
            entity.setCreePar(null);
            new AuditEntityListener().avantCreation(entity);

            // keycloakService null → fallback "system"
            assertThat(entity.getCreePar()).isEqualTo("system");
        }
    }

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("obtenirUtilisateurCourant: authentifié mais email blank → retourne 'system' (L95 branche email.isBlank)")
    void avantCreation_authenticated_blankEmail_returnsSystem() {
        ArcContainer mockContainer = mock(ArcContainer.class);
        InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
        KeycloakService mockService = mock(KeycloakService.class);

        when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
        when(mockHandle.get()).thenReturn(mockService);
        when(mockService.isAuthenticated()).thenReturn(true);
        // email blank → L95 condition !email.isBlank() = false → ne retourne pas email → tombe sur "system"
        when(mockService.getCurrentUserEmail()).thenReturn("   ");

        try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
            arcMock.when(Arc::container).thenReturn(mockContainer);

            BaseEntity entity = new Adresse();
            entity.setCreePar(null);
            new AuditEntityListener().avantCreation(entity);

            assertThat(entity.getCreePar()).isEqualTo("system");
        }
    }

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("obtenirUtilisateurCourant: authentifié mais email null → retourne 'system' (L95 branche email==null)")
    void avantCreation_authenticated_nullEmail_returnsSystem() {
        ArcContainer mockContainer = mock(ArcContainer.class);
        InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
        KeycloakService mockService = mock(KeycloakService.class);

        when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
        when(mockHandle.get()).thenReturn(mockService);
        when(mockService.isAuthenticated()).thenReturn(true);
        // email null → L95 condition (email != null && !email.isBlank()) = false → retourne "system"
        when(mockService.getCurrentUserEmail()).thenReturn(null);

        try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
            arcMock.when(Arc::container).thenReturn(mockContainer);

            BaseEntity entity = new Adresse();
            entity.setCreePar(null);
            new AuditEntityListener().avantCreation(entity);

            assertThat(entity.getCreePar()).isEqualTo("system");
        }
    }

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("obtenirUtilisateurCourant: non authentifié → retourne 'system' (L93 branche isAuthenticated=false)")
    void avantCreation_notAuthenticated_returnsSystem() {
        ArcContainer mockContainer = mock(ArcContainer.class);
        InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
        KeycloakService mockService = mock(KeycloakService.class);

        when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
        when(mockHandle.get()).thenReturn(mockService);
        // isAuthenticated=false → L93 condition false → ne rentre pas dans le if → retourne "system"
        when(mockService.isAuthenticated()).thenReturn(false);

        try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
            arcMock.when(Arc::container).thenReturn(mockContainer);

            BaseEntity entity = new Adresse();
            entity.setCreePar(null);
            new AuditEntityListener().avantCreation(entity);

            assertThat(entity.getCreePar()).isEqualTo("system");
        }
    }
}
