package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Organisation;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.TestTransaction;
import jakarta.inject.Inject;
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;

/**
 * Teste les méthodes héritées de BaseRepository via OrganisationRepository.
 * BaseRepository est abstrait, donc on s'appuie sur une implémentation concrète.
 */
@QuarkusTest
class BaseRepositoryTest {

    @Inject
    OrganisationRepository organisationRepository;

    private static Organisation newOrganisation(String email) {
        Organisation o = new Organisation();
        o.setNom("Org Test");
        o.setTypeOrganisation("ASSOCIATION");
        o.setStatut("ACTIVE");
        o.setEmail(email);
        o.setActif(true);
        return o;
    }

    @Test
    @TestTransaction
    @DisplayName("findById retourne null pour UUID inexistant")
    void findById_inexistant_returnsNull() {
        Organisation found = organisationRepository.findById(UUID.randomUUID());
        assertThat(found).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("findByIdOptional retourne empty pour UUID inexistant")
    void findByIdOptional_inexistant_returnsEmpty() {
        Optional<Organisation> opt = organisationRepository.findByIdOptional(UUID.randomUUID());
        assertThat(opt).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("persist puis findById retrouve l'entité")
    void persist_thenFindById_findsEntity() {
        Organisation o = newOrganisation("base-repo-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        assertThat(o.getId()).isNotNull();
        Organisation found = organisationRepository.findById(o.getId());
        assertThat(found).isNotNull();
        assertThat(found.getEmail()).isEqualTo(o.getEmail());
    }

    @Test
    @TestTransaction
    @DisplayName("findByIdOptional retrouve l'entité persistée")
    void findByIdOptional_findsPersisted() {
        Organisation o = newOrganisation("opt-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        Optional<Organisation> opt = organisationRepository.findByIdOptional(o.getId());
        assertThat(opt).isPresent();
        assertThat(opt.get().getNom()).isEqualTo(o.getNom());
    }

    @Test
    @TestTransaction
    @DisplayName("update modifie l'entité")
    void update_modifiesEntity() {
        Organisation o = newOrganisation("upd-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        o.setNom("Nom modifié");
        organisationRepository.update(o);
        Organisation found = organisationRepository.findById(o.getId());
        assertThat(found).isNotNull();
        assertThat(found.getNom()).isEqualTo("Nom modifié");
    }

    @Test
    @TestTransaction
    @DisplayName("delete supprime l'entité")
    void delete_removesEntity() {
        Organisation o = newOrganisation("del-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        UUID id = o.getId();
        organisationRepository.delete(o);
        assertThat(organisationRepository.findById(id)).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("deleteById retourne true et supprime quand l'entité existe")
    void deleteById_existing_returnsTrueAndRemoves() {
        Organisation o = newOrganisation("delid-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        UUID id = o.getId();
        boolean deleted = organisationRepository.deleteById(id);
        assertThat(deleted).isTrue();
        assertThat(organisationRepository.findById(id)).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("deleteById retourne false quand l'entité n'existe pas")
    void deleteById_inexistant_returnsFalse() {
        boolean deleted = organisationRepository.deleteById(UUID.randomUUID());
        assertThat(deleted).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("listAll retourne une liste (éventuellement vide)")
    void listAll_returnsList() {
        List<Organisation> all = organisationRepository.listAll();
        assertThat(all).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("findAll avec Page et Sort null retourne une liste paginée")
    void findAll_pageAndSortNull_returnsPagedList() {
        Page page = new Page(0, 10);
        List<Organisation> list = organisationRepository.findAll(page, null);
        assertThat(list).isNotNull();
        assertThat(list.size()).isLessThanOrEqualTo(10);
    }

    @Test
    @TestTransaction
    @DisplayName("findAll avec Page et Sort par nom retourne une liste triée")
    void findAll_pageAndSort_returnsSortedList() {
        Page page = new Page(0, 10);
        Sort sort = Sort.by("nom").ascending();
        List<Organisation> list = organisationRepository.findAll(page, sort);
        assertThat(list).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("count retourne un nombre >= 0")
    void count_returnsNonNegative() {
        long n = organisationRepository.count();
        assertThat(n).isGreaterThanOrEqualTo(0L);
    }

    @Test
    @TestTransaction
    @DisplayName("existsById retourne false pour UUID inexistant")
    void existsById_inexistant_returnsFalse() {
        boolean exists = organisationRepository.existsById(UUID.randomUUID());
        assertThat(exists).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("existsById retourne true après persist")
    void existsById_afterPersist_returnsTrue() {
        Organisation o = newOrganisation("exists-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        assertThat(organisationRepository.existsById(o.getId())).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("getEntityManager retourne un EntityManager non null")
    void getEntityManager_returnsNonNull() {
        assertThat(organisationRepository.getEntityManager()).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("persist avec entité ayant un id existant effectue un merge")
    void persist_withExistingId_performsMerge() {
        Organisation o = newOrganisation("merge-" + UUID.randomUUID() + "@test.com");
        organisationRepository.persist(o);
        UUID id = o.getId();
        o.setNom("Nom après merge");
        organisationRepository.persist(o);
        Organisation found = organisationRepository.findById(id);
        assertThat(found).isNotNull();
        assertThat(found.getNom()).isEqualTo("Nom après merge");
    }

    @Test
    @TestTransaction
    @DisplayName("delete avec entité null ne lève pas d'exception")
    void delete_null_doesNotThrow() {
        organisationRepository.delete(null);
    }
}
