package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.api.enums.membre.NiveauRisqueKyc;
import dev.lions.unionflow.server.api.enums.membre.StatutKyc;
import dev.lions.unionflow.server.entity.KycDossier;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@DisplayName("KycDossierRepository — tests unitaires")
class KycDossierRepositoryTest {

    static class TestableKycDossierRepository extends KycDossierRepository {
        // instanciation sans CDI
    }

    private KycDossierRepository repo;

    @BeforeEach
    void setUp() {
        repo = spy(new TestableKycDossierRepository());
    }

    @Test
    @DisplayName("classExists : instanciation possible sans CDI")
    void classExists() {
        assertThat(new TestableKycDossierRepository()).isNotNull();
    }

    // ─── findDossierActifByMembre ─────────────────────────────────────────────

    @Test
    @DisplayName("findDossierActifByMembre : retourne Optional.empty() si aucun dossier")
    @SuppressWarnings("unchecked")
    void findDossierActifByMembre_noResult_returnsEmpty() {
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), any(UUID.class));

        Optional<KycDossier> result = repo.findDossierActifByMembre(UUID.randomUUID());

        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("findDossierActifByMembre : retourne le dossier actif du membre")
    @SuppressWarnings("unchecked")
    void findDossierActifByMembre_withResult_returnsDossier() {
        KycDossier dossier = new KycDossier();
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(dossier));
        doReturn(query).when(repo).find(anyString(), any(UUID.class));

        Optional<KycDossier> result = repo.findDossierActifByMembre(UUID.randomUUID());

        assertThat(result).isPresent().contains(dossier);
    }

    // ─── findByMembre ─────────────────────────────────────────────────────────

    @Test
    @DisplayName("findByMembre : retourne la liste des dossiers du membre")
    @SuppressWarnings("unchecked")
    void findByMembre_returnsList() {
        KycDossier d1 = new KycDossier();
        KycDossier d2 = new KycDossier();
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of(d1, d2));
        doReturn(query).when(repo).find(anyString(), any(UUID.class));

        List<KycDossier> result = repo.findByMembre(UUID.randomUUID());

        assertThat(result).hasSize(2);
    }

    @Test
    @DisplayName("findByMembre : retourne liste vide si aucun dossier")
    @SuppressWarnings("unchecked")
    void findByMembre_noResult_returnsEmpty() {
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString(), any(UUID.class));

        List<KycDossier> result = repo.findByMembre(UUID.randomUUID());

        assertThat(result).isEmpty();
    }

    // ─── findByStatut ─────────────────────────────────────────────────────────

    @Test
    @DisplayName("findByStatut : retourne les dossiers du statut demandé")
    @SuppressWarnings("unchecked")
    void findByStatut_returnsList() {
        KycDossier d = new KycDossier();
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of(d));
        doReturn(query).when(repo).find(anyString(), any(StatutKyc.class));

        List<KycDossier> result = repo.findByStatut(StatutKyc.EN_COURS);

        assertThat(result).hasSize(1);
    }

    @Test
    @DisplayName("findByStatut : tous les StatutKyc couverts")
    void findByStatut_allStatutValues() {
        assertThat(StatutKyc.NON_VERIFIE.getLibelle()).isEqualTo("Non vérifié");
        assertThat(StatutKyc.EN_COURS.getLibelle()).isEqualTo("En cours");
        assertThat(StatutKyc.VERIFIE.getLibelle()).isEqualTo("Vérifié");
        assertThat(StatutKyc.REFUSE.getLibelle()).isEqualTo("Refusé");
    }

    // ─── findByNiveauRisque ───────────────────────────────────────────────────

    @Test
    @DisplayName("findByNiveauRisque : retourne les dossiers du niveau de risque")
    @SuppressWarnings("unchecked")
    void findByNiveauRisque_returnsList() {
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString(), any(NiveauRisqueKyc.class));

        List<KycDossier> result = repo.findByNiveauRisque(NiveauRisqueKyc.ELEVE);

        assertThat(result).isNotNull();
    }

    @Test
    @DisplayName("NiveauRisqueKyc : toutes les valeurs accessibles")
    void niveauRisqueEnum_allValues() {
        assertThat(NiveauRisqueKyc.FAIBLE.getLibelle()).isEqualTo("Risque faible");
        assertThat(NiveauRisqueKyc.MOYEN.getLibelle()).isEqualTo("Risque moyen");
        assertThat(NiveauRisqueKyc.ELEVE.getLibelle()).isEqualTo("Risque élevé");
        assertThat(NiveauRisqueKyc.CRITIQUE.getLibelle()).isEqualTo("Risque critique");

        assertThat(NiveauRisqueKyc.fromScore(20)).isEqualTo(NiveauRisqueKyc.FAIBLE);
        assertThat(NiveauRisqueKyc.fromScore(55)).isEqualTo(NiveauRisqueKyc.MOYEN);
        assertThat(NiveauRisqueKyc.fromScore(75)).isEqualTo(NiveauRisqueKyc.ELEVE);
        assertThat(NiveauRisqueKyc.fromScore(95)).isEqualTo(NiveauRisqueKyc.CRITIQUE);
        // score hors plage → CRITIQUE
        assertThat(NiveauRisqueKyc.fromScore(999)).isEqualTo(NiveauRisqueKyc.CRITIQUE);
    }

    // ─── findPep ─────────────────────────────────────────────────────────────

    @Test
    @DisplayName("findPep : retourne les dossiers PEP actifs")
    @SuppressWarnings("unchecked")
    void findPep_returnsList() {
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString());

        List<KycDossier> result = repo.findPep();

        assertThat(result).isNotNull();
    }

    // ─── findPiecesExpirantsAvant ─────────────────────────────────────────────

    @Test
    @DisplayName("findPiecesExpirantsAvant : retourne les dossiers avec pièce expirant avant la date")
    @SuppressWarnings("unchecked")
    void findPiecesExpirantsAvant_returnsList() {
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString(), any(LocalDate.class));

        List<KycDossier> result = repo.findPiecesExpirantsAvant(LocalDate.now().plusDays(30));

        assertThat(result).isNotNull();
    }

    // ─── countByStatut ────────────────────────────────────────────────────────

    @Test
    @DisplayName("countByStatut : retourne le nombre de dossiers du statut")
    void countByStatut_returnsCount() {
        doReturn(3L).when(repo).count(anyString(), any(StatutKyc.class));

        long result = repo.countByStatut(StatutKyc.VERIFIE);

        assertThat(result).isEqualTo(3L);
    }

    // ─── countPepActifs ───────────────────────────────────────────────────────

    @Test
    @DisplayName("countPepActifs : retourne le nombre de PEP actifs")
    void countPepActifs_returnsCount() {
        doReturn(2L).when(repo).count(anyString());

        long result = repo.countPepActifs();

        assertThat(result).isEqualTo(2L);
    }

    // ─── findByAnnee ──────────────────────────────────────────────────────────

    @Test
    @DisplayName("findByAnnee : retourne les dossiers de l'année de référence")
    @SuppressWarnings("unchecked")
    void findByAnnee_returnsList() {
        KycDossier d = new KycDossier();
        PanacheQuery<KycDossier> query = mock(PanacheQuery.class);
        when(query.list()).thenReturn(List.of(d));
        doReturn(query).when(repo).find(anyString(), eq(2025));

        List<KycDossier> result = repo.findByAnnee(2025);

        assertThat(result).hasSize(1);
    }
}
