package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.api.enums.abonnement.PlageMembres;
import dev.lions.unionflow.server.api.enums.abonnement.TypeFormule;
import dev.lions.unionflow.server.entity.FormuleAbonnement;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@DisplayName("FormuleAbonnementRepository — tests unitaires")
class FormuleAbonnementRepositoryTest {

    static class TestableFormuleAbonnementRepository extends FormuleAbonnementRepository {
        TestableFormuleAbonnementRepository(EntityManager em) {
            super();
            this.entityManager = em;
        }
    }

    private EntityManager em;
    private TestableFormuleAbonnementRepository repo;

    @BeforeEach
    void setUp() {
        em = mock(EntityManager.class);
        repo = spy(new TestableFormuleAbonnementRepository(em));
    }

    @Test
    @DisplayName("constructeur : initialise entityClass à FormuleAbonnement")
    void constructor_setsEntityClass() {
        assertThat(repo.entityClass).isEqualTo(FormuleAbonnement.class);
    }

    // ─── findByCodeAndPlage ───────────────────────────────────────────────────

    @Test
    @DisplayName("findByCodeAndPlage : retourne Optional.empty() si aucune formule")
    @SuppressWarnings("unchecked")
    void findByCodeAndPlage_noResult_returnsEmpty() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), any(TypeFormule.class), any(PlageMembres.class));

        Optional<FormuleAbonnement> result =
                repo.findByCodeAndPlage(TypeFormule.BASIC, PlageMembres.PETITE);

        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("findByCodeAndPlage : retourne la formule correspondante")
    @SuppressWarnings("unchecked")
    void findByCodeAndPlage_withResult_returnsFormule() {
        FormuleAbonnement formule = new FormuleAbonnement();
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(formule));
        doReturn(query).when(repo).find(anyString(), any(TypeFormule.class), any(PlageMembres.class));

        Optional<FormuleAbonnement> result =
                repo.findByCodeAndPlage(TypeFormule.PREMIUM, PlageMembres.GRANDE);

        assertThat(result).isPresent();
        assertThat(result.get()).isSameAs(formule);
    }

    // ─── findAllActifOrderByOrdre ─────────────────────────────────────────────

    @Test
    @DisplayName("findAllActifOrderByOrdre : retourne liste vide si aucune formule active")
    @SuppressWarnings("unchecked")
    void findAllActifOrderByOrdre_noResult_returnsEmpty() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString());

        List<FormuleAbonnement> result = repo.findAllActifOrderByOrdre();

        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("findAllActifOrderByOrdre : retourne la liste des formules actives")
    @SuppressWarnings("unchecked")
    void findAllActifOrderByOrdre_withResult_returnsList() {
        FormuleAbonnement f1 = new FormuleAbonnement();
        FormuleAbonnement f2 = new FormuleAbonnement();
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.list()).thenReturn(List.of(f1, f2));
        doReturn(query).when(repo).find(anyString());

        List<FormuleAbonnement> result = repo.findAllActifOrderByOrdre();

        assertThat(result).hasSize(2).containsExactly(f1, f2);
    }

    // ─── findByPlage ──────────────────────────────────────────────────────────

    @Test
    @DisplayName("findByPlage : retourne liste vide si aucune formule pour cette plage")
    @SuppressWarnings("unchecked")
    void findByPlage_noResult_returnsEmpty() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString(), any(PlageMembres.class));

        List<FormuleAbonnement> result = repo.findByPlage(PlageMembres.TRES_GRANDE);

        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("findByPlage : retourne les formules de la plage")
    @SuppressWarnings("unchecked")
    void findByPlage_withResult_returnsList() {
        FormuleAbonnement formule = new FormuleAbonnement();
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.list()).thenReturn(List.of(formule));
        doReturn(query).when(repo).find(anyString(), any(PlageMembres.class));

        List<FormuleAbonnement> result = repo.findByPlage(PlageMembres.MOYENNE);

        assertThat(result).hasSize(1);
    }

    // ─── findByCode ───────────────────────────────────────────────────────────

    @Test
    @DisplayName("findByCode : retourne liste vide si aucune formule pour ce niveau")
    @SuppressWarnings("unchecked")
    void findByCode_noResult_returnsEmpty() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.list()).thenReturn(List.of());
        doReturn(query).when(repo).find(anyString(), any(TypeFormule.class));

        List<FormuleAbonnement> result = repo.findByCode(TypeFormule.STANDARD);

        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("findByCode : retourne les formules du niveau donné")
    @SuppressWarnings("unchecked")
    void findByCode_withResult_returnsList() {
        FormuleAbonnement f1 = new FormuleAbonnement();
        FormuleAbonnement f2 = new FormuleAbonnement();
        FormuleAbonnement f3 = new FormuleAbonnement();
        io.quarkus.hibernate.orm.panache.PanacheQuery<FormuleAbonnement> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.list()).thenReturn(List.of(f1, f2, f3));
        doReturn(query).when(repo).find(anyString(), any(TypeFormule.class));

        List<FormuleAbonnement> result = repo.findByCode(TypeFormule.PREMIUM);

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