package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Organisation;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

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

/**
 * Tests unitaires directs pour les méthodes de BaseRepository dont
 * Quarkus génère des implémentations alternatives dans les sous-classes concrètes
 * (findByIdOptional, deleteById, listAll, count, getEntityManager).
 * Ces tests instancient un sous-type de test sans CDI/Quarkus.
 */
@SuppressWarnings({"unchecked", "rawtypes"})
@DisplayName("BaseRepository — méthodes non enhancées par Quarkus")
class BaseRepositoryUnitTest {

    /** Sous-classe concrète de test — non gérée par CDI. */
    @SuppressWarnings("unchecked")
    static class TestRepo extends BaseRepository<Organisation> {
        private final PanacheQuery<Organisation> mockQuery;

        TestRepo(EntityManager em, PanacheQuery<Organisation> mockQuery) {
            super(Organisation.class);
            this.entityManager = em;
            this.mockQuery = mockQuery;
        }

        TestRepo(EntityManager em) {
            this(em, null);
        }

        /** Surcharge findAll() pour éviter d'appeler le runtime Panache/Quarkus. */
        @Override
        public PanacheQuery<Organisation> findAll() {
            return mockQuery;
        }
    }

    @Test
    @DisplayName("findByIdOptional: retourne Optional.of(entity) quand trouvée")
    void findByIdOptional_found_returnsPresent() {
        EntityManager em = mock(EntityManager.class);
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        when(em.find(Organisation.class, id)).thenReturn(org);

        TestRepo repo = new TestRepo(em);
        Optional<Organisation> result = repo.findByIdOptional(id);
        assertThat(result).isPresent().contains(org);
    }

    @Test
    @DisplayName("findByIdOptional: retourne Optional.empty() quand non trouvée")
    void findByIdOptional_notFound_returnsEmpty() {
        EntityManager em = mock(EntityManager.class);
        UUID id = UUID.randomUUID();
        when(em.find(Organisation.class, id)).thenReturn(null);

        TestRepo repo = new TestRepo(em);
        Optional<Organisation> result = repo.findByIdOptional(id);
        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("listAll: délègue à findAll().list()")
    void listAll_delegatesToFindAll() {
        EntityManager em = mock(EntityManager.class);
        PanacheQuery<Organisation> mockQuery = mock(PanacheQuery.class);
        Organisation org = new Organisation();
        when(mockQuery.list()).thenReturn(List.of(org));

        TestRepo repo = new TestRepo(em, mockQuery);
        List<Organisation> result = repo.listAll();
        assertThat(result).containsExactly(org);
    }

    @Test
    @DisplayName("count: délègue à findAll().count()")
    void count_delegatesToFindAll() {
        EntityManager em = mock(EntityManager.class);
        PanacheQuery<Organisation> mockQuery = mock(PanacheQuery.class);
        when(mockQuery.count()).thenReturn(42L);

        TestRepo repo = new TestRepo(em, mockQuery);
        assertThat(repo.count()).isEqualTo(42L);
    }

    @Test
    @DisplayName("getEntityManager: retourne l'EntityManager injecté")
    void getEntityManager_returnsInjectedEntityManager() {
        EntityManager em = mock(EntityManager.class);
        TestRepo repo = new TestRepo(em);
        assertThat(repo.getEntityManager()).isSameAs(em);
    }

    @Test
    @DisplayName("deleteById: retourne false quand entité non trouvée")
    void deleteById_notFound_returnsFalse() {
        EntityManager em = mock(EntityManager.class);
        UUID id = UUID.randomUUID();
        when(em.find(Organisation.class, id)).thenReturn(null);

        TestRepo repo = new TestRepo(em);
        assertThat(repo.deleteById(id)).isFalse();
    }

    @Test
    @DisplayName("deleteById: retourne true et supprime quand entité trouvée")
    void deleteById_found_returnsTrueAndDeletes() {
        EntityManager em = mock(EntityManager.class);
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        when(em.find(Organisation.class, id)).thenReturn(org);
        when(em.contains(org)).thenReturn(true);

        TestRepo repo = new TestRepo(em);
        boolean result = repo.deleteById(id);
        assertThat(result).isTrue();
        verify(em).remove(org);
    }
}
