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.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
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;

/**
 * Tests couvrant les méthodes de BaseRepository via OrganisationRepository (qui l'étend).
 */
@QuarkusTest
class BaseRepositoryDirectTest {

    @Inject
    OrganisationRepository repo;

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

    @Test
    @TestTransaction
    @DisplayName("listAll retourne une liste non-null")
    void listAll_returnsNonNull() {
        List<Organisation> list = repo.listAll();
        assertThat(list).isNotNull();
    }

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

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

    @Test
    @TestTransaction
    @DisplayName("deleteById sur UUID inexistant retourne false")
    void deleteById_inexistant_returnsFalse() {
        boolean deleted = repo.deleteById(UUID.randomUUID());
        assertThat(deleted).isFalse();
    }

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

    @Test
    @TestTransaction
    @DisplayName("findAll avec page et sort null retourne une liste")
    void findAll_withPageAndNullSort_returnsList() {
        List<Organisation> list = repo.findAll(Page.of(0, 10), null);
        assertThat(list).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("findAll avec page et sort non-null retourne une liste")
    void findAll_withPageAndSort_returnsList() {
        List<Organisation> list = repo.findAll(Page.of(0, 10), Sort.by("nom", Sort.Direction.Ascending));
        assertThat(list).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("persist puis findById puis deleteById puis findById retourne null")
    void persist_then_deleteById_returnsTrue() {
        Organisation org = new Organisation();
        org.setNom("Base Repo Test " + UUID.randomUUID());
        org.setEmail("base-repo-" + UUID.randomUUID() + "@test.com");
        org.setTypeOrganisation("ASSOCIATION");
        org.setStatut("ACTIVE");
        org.setActif(true);

        repo.persist(org);
        assertThat(org.getId()).isNotNull();

        boolean deleted = repo.deleteById(org.getId());
        assertThat(deleted).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("update retourne l'entité mise à jour")
    void update_returnsUpdatedEntity() {
        Organisation org = new Organisation();
        org.setNom("Update Test " + UUID.randomUUID());
        org.setEmail("update-" + UUID.randomUUID() + "@test.com");
        org.setTypeOrganisation("ASSOCIATION");
        org.setStatut("ACTIVE");
        org.setActif(true);

        repo.persist(org);
        org.setNom("Nom Modifie");
        Organisation updated = repo.update(org);
        assertThat(updated).isNotNull();
        assertThat(updated.getNom()).isEqualTo("Nom Modifie");
    }

    @Test
    @TestTransaction
    @DisplayName("delete sur entité persistée la supprime")
    void delete_persistedEntity_removesIt() {
        Organisation org = new Organisation();
        org.setNom("Delete Test " + UUID.randomUUID());
        org.setEmail("delete-" + UUID.randomUUID() + "@test.com");
        org.setTypeOrganisation("ASSOCIATION");
        org.setStatut("ACTIVE");
        org.setActif(true);

        repo.persist(org);
        UUID id = org.getId();
        assertThat(id).isNotNull();

        repo.delete(org);
        Organisation found = repo.findById(id);
        assertThat(found).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("persist avec ID déjà défini utilise merge")
    void persist_withExistingId_usesMerge() {
        Organisation org = new Organisation();
        org.setNom("Merge Test " + UUID.randomUUID());
        org.setEmail("merge-" + UUID.randomUUID() + "@test.com");
        org.setTypeOrganisation("ASSOCIATION");
        org.setStatut("ACTIVE");
        org.setActif(true);

        repo.persist(org);
        UUID id = org.getId();

        org.setNom("Nom Apres Merge");
        repo.persist(org); // doit utiliser merge car id != null

        Organisation found = repo.findById(id);
        assertThat(found).isNotNull();
        assertThat(found.getNom()).isEqualTo("Nom Apres Merge");
    }

    @Test
    @TestTransaction
    @DisplayName("delete null ne lance pas d'exception")
    void delete_null_noException() {
        // delete(null) doit être géré sans NPE
        repo.delete(null);
    }
}
