package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.paiement.StatutIntentionPaiement;
import dev.lions.unionflow.server.api.enums.paiement.TypeObjetIntentionPaiement;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("IntentionPaiement")
class IntentionPaiementTest {

    private static Membre newMembre() {
        Membre m = new Membre();
        m.setId(UUID.randomUUID());
        return m;
    }

    @Test
    @DisplayName("getters/setters")
    void gettersSetters() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(new BigDecimal("5000.00"));
        i.setCodeDevise("XOF");
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        i.setStatut(StatutIntentionPaiement.INITIEE);
        i.setWaveCheckoutSessionId("sess-1");
        i.setWaveLaunchUrl("https://wave.com/pay");
        i.setObjetsCibles("[{\"type\":\"COTISATION\"}]");
        i.setDateExpiration(LocalDateTime.now().plusMinutes(30));

        assertThat(i.getMontantTotal()).isEqualByComparingTo("5000.00");
        assertThat(i.getCodeDevise()).isEqualTo("XOF");
        assertThat(i.getTypeObjet()).isEqualTo(TypeObjetIntentionPaiement.COTISATION);
        assertThat(i.getStatut()).isEqualTo(StatutIntentionPaiement.INITIEE);
        assertThat(i.getWaveCheckoutSessionId()).isEqualTo("sess-1");
    }

    @Test
    @DisplayName("statut et codeDevise par défaut")
    void defauts() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        assertThat(i.getStatut()).isEqualTo(StatutIntentionPaiement.INITIEE);
        assertThat(i.getCodeDevise()).isEqualTo("XOF");
    }

    @Test
    @DisplayName("isActive: true si INITIEE ou EN_COURS")
    void isActive() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        i.setStatut(StatutIntentionPaiement.INITIEE);
        assertThat(i.isActive()).isTrue();
        i.setStatut(StatutIntentionPaiement.EN_COURS);
        assertThat(i.isActive()).isTrue();
        i.setStatut(StatutIntentionPaiement.COMPLETEE);
        assertThat(i.isActive()).isFalse();
    }

    @Test
    @DisplayName("isExpiree: true si dateExpiration dans le passé")
    void isExpiree() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        i.setDateExpiration(LocalDateTime.now().minusMinutes(1));
        assertThat(i.isExpiree()).isTrue();
        i.setDateExpiration(LocalDateTime.now().plusHours(1));
        assertThat(i.isExpiree()).isFalse();
    }

    @Test
    @DisplayName("isCompletee")
    void isCompletee() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        i.setStatut(StatutIntentionPaiement.COMPLETEE);
        assertThat(i.isCompletee()).isTrue();
        i.setStatut(StatutIntentionPaiement.INITIEE);
        assertThat(i.isCompletee()).isFalse();
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        Membre m = newMembre();
        IntentionPaiement a = new IntentionPaiement();
        a.setId(id);
        a.setUtilisateur(m);
        a.setMontantTotal(new BigDecimal("100"));
        a.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        IntentionPaiement b = new IntentionPaiement();
        b.setId(id);
        b.setUtilisateur(m);
        b.setMontantTotal(new BigDecimal("100"));
        b.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        assertThat(i.toString()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("isExpiree: false si dateExpiration null")
    void isExpiree_nullDate_returnsFalse() {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        i.setDateExpiration(null);
        assertThat(i.isExpiree()).isFalse();
    }

    @Test
    @DisplayName("onCreate: initialise les défauts si null")
    void onCreate_setsDefaults() throws Exception {
        IntentionPaiement i = new IntentionPaiement();
        i.setUtilisateur(newMembre());
        i.setMontantTotal(BigDecimal.ONE);
        i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
        i.setStatut(null);
        i.setCodeDevise(null);
        i.setDateExpiration(null);

        Method onCreate = IntentionPaiement.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(i);

        assertThat(i.getStatut()).isEqualTo(StatutIntentionPaiement.INITIEE);
        assertThat(i.getCodeDevise()).isEqualTo("XOF");
        assertThat(i.getDateExpiration()).isNotNull().isAfter(LocalDateTime.now());
    }
}
