package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.api.enums.paiement.StatutIntentionPaiement;
import dev.lions.unionflow.server.api.enums.paiement.TypeObjetIntentionPaiement;
import dev.lions.unionflow.server.entity.IntentionPaiement;
import dev.lions.unionflow.server.entity.Membre;
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.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;

@QuarkusTest
@DisplayName("IntentionPaiementRepository — tests des méthodes de requête")
class IntentionPaiementRepositoryTest {

    @Inject
    IntentionPaiementRepository intentionRepository;

    @Inject
    EntityManager em;

    // ─── Helpers ─────────────────────────────────────────────────────────────

    private Membre persistMembre() {
        Membre m = new Membre();
        m.setNumeroMembre("IP-MEM-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase());
        m.setPrenom("Ibrahima");
        m.setNom("PaiementTest");
        m.setEmail("ip." + UUID.randomUUID() + "@test.com");
        m.setDateNaissance(LocalDate.of(1985, 6, 15));
        m.setActif(true);
        em.persist(m);
        em.flush();
        return m;
    }

    private IntentionPaiement persistIntention(Membre utilisateur, String sessionId) {
        IntentionPaiement intention = IntentionPaiement.builder()
                .utilisateur(utilisateur)
                .montantTotal(BigDecimal.valueOf(5000))
                .typeObjet(TypeObjetIntentionPaiement.COTISATION)
                .statut(StatutIntentionPaiement.INITIEE)
                .build();
        intention.setWaveCheckoutSessionId(sessionId);
        intention.setActif(true);
        em.persist(intention);
        em.flush();
        return intention;
    }

    // ─── Tests findByWaveCheckoutSessionId ──────────────────────────────────

    @Test
    @TestTransaction
    @DisplayName("findByWaveCheckoutSessionId retourne l'intention avec le sessionId correspondant")
    void findByWaveCheckoutSessionId_sessionIdExistant_retourneIntention() {
        Membre membre = persistMembre();
        String sessionId = "wave-session-" + UUID.randomUUID();
        persistIntention(membre, sessionId);

        Optional<IntentionPaiement> result = intentionRepository.findByWaveCheckoutSessionId(sessionId);

        assertThat(result).isPresent();
        assertThat(result.get().getWaveCheckoutSessionId()).isEqualTo(sessionId);
    }

    @Test
    @TestTransaction
    @DisplayName("findByWaveCheckoutSessionId retourne empty pour sessionId inexistant")
    void findByWaveCheckoutSessionId_sessionIdInexistant_retourneEmpty() {
        Optional<IntentionPaiement> result = intentionRepository.findByWaveCheckoutSessionId("session-inexistante");

        assertThat(result).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByWaveCheckoutSessionId retourne empty pour sessionId null")
    void findByWaveCheckoutSessionId_null_retourneEmpty() {
        Optional<IntentionPaiement> result = intentionRepository.findByWaveCheckoutSessionId(null);

        assertThat(result).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByWaveCheckoutSessionId retourne empty pour sessionId blank")
    void findByWaveCheckoutSessionId_blank_retourneEmpty() {
        Optional<IntentionPaiement> result = intentionRepository.findByWaveCheckoutSessionId("   ");

        assertThat(result).isEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByWaveCheckoutSessionId retourne empty pour chaîne vide")
    void findByWaveCheckoutSessionId_chaineVide_retourneEmpty() {
        Optional<IntentionPaiement> result = intentionRepository.findByWaveCheckoutSessionId("");

        assertThat(result).isEmpty();
    }

    // ─── Tests méthodes métier ───────────────────────────────────────────────

    @Test
    @TestTransaction
    @DisplayName("isActive retourne true pour statut INITIEE")
    void isActive_statutInitiee_retourneTrue() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setStatut(StatutIntentionPaiement.INITIEE);

        assertThat(intention.isActive()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("isActive retourne true pour statut EN_COURS")
    void isActive_statutEnCours_retourneTrue() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setStatut(StatutIntentionPaiement.EN_COURS);

        assertThat(intention.isActive()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("isActive retourne false pour statut COMPLETEE")
    void isActive_statutCompletee_retourneFalse() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setStatut(StatutIntentionPaiement.COMPLETEE);

        assertThat(intention.isActive()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("isExpiree retourne true quand dateExpiration est dans le passé")
    void isExpiree_dateExpirEePassee_retourneTrue() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setDateExpiration(LocalDateTime.now().minusMinutes(5));

        assertThat(intention.isExpiree()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("isExpiree retourne false quand dateExpiration est dans le futur")
    void isExpiree_dateExpirationFutur_retourneFalse() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setDateExpiration(LocalDateTime.now().plusHours(1));

        assertThat(intention.isExpiree()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("isExpiree retourne false quand dateExpiration est null")
    void isExpiree_dateExpirationNull_retourneFalse() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setDateExpiration(null);

        assertThat(intention.isExpiree()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("isCompletee retourne true pour statut COMPLETEE")
    void isCompletee_statutCompletee_retourneTrue() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setStatut(StatutIntentionPaiement.COMPLETEE);

        assertThat(intention.isCompletee()).isTrue();
    }

    @Test
    @TestTransaction
    @DisplayName("isCompletee retourne false pour statut INITIEE")
    void isCompletee_statutInitiee_retourneFalse() {
        Membre membre = persistMembre();
        IntentionPaiement intention = persistIntention(membre, "s-" + UUID.randomUUID());
        intention.setStatut(StatutIntentionPaiement.INITIEE);

        assertThat(intention.isCompletee()).isFalse();
    }

    @Test
    @TestTransaction
    @DisplayName("persist et findById fonctionnent correctement")
    void persistAndFindById_retourneIntentionPersistee() {
        Membre membre = persistMembre();
        String sessionId = "wave-persist-" + UUID.randomUUID();
        IntentionPaiement intention = persistIntention(membre, sessionId);

        IntentionPaiement found = intentionRepository.findById(intention.getId());

        assertThat(found).isNotNull();
        assertThat(found.getMontantTotal()).isEqualByComparingTo(BigDecimal.valueOf(5000));
        assertThat(found.getTypeObjet()).isEqualTo(TypeObjetIntentionPaiement.COTISATION);
    }
}
