package dev.lions.unionflow.server.entity;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("DemandeAdhesion")
class DemandeAdhesionTest {

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

    private static Organisation newOrganisation() {
        Organisation o = new Organisation();
        o.setId(UUID.randomUUID());
        return o;
    }

    @Test
    @DisplayName("getters/setters")
    void gettersSetters() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setNumeroReference("DA-001");
        d.setUtilisateur(newMembre());
        d.setOrganisation(newOrganisation());
        d.setStatut("APPROUVEE");
        d.setFraisAdhesion(new BigDecimal("5000.00"));
        d.setMontantPaye(new BigDecimal("5000.00"));
        d.setCodeDevise("XOF");
        LocalDateTime dt = LocalDateTime.now();
        d.setDateDemande(dt);
        d.setDateTraitement(dt);

        assertThat(d.getNumeroReference()).isEqualTo("DA-001");
        assertThat(d.getStatut()).isEqualTo("APPROUVEE");
        assertThat(d.getFraisAdhesion()).isEqualByComparingTo("5000.00");
        assertThat(d.getMontantPaye()).isEqualByComparingTo("5000.00");
        assertThat(d.getCodeDevise()).isEqualTo("XOF");
        assertThat(d.getDateDemande()).isEqualTo(dt);
        assertThat(d.getDateTraitement()).isEqualTo(dt);
    }

    @Test
    @DisplayName("statut et codeDevise par défaut")
    void defauts() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setNumeroReference("x");
        d.setUtilisateur(newMembre());
        d.setOrganisation(newOrganisation());
        assertThat(d.getStatut()).isEqualTo("EN_ATTENTE");
        assertThat(d.getCodeDevise()).isEqualTo("XOF");
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        LocalDateTime sameDate = LocalDateTime.now();
        Membre m = newMembre();
        Organisation o = newOrganisation();
        DemandeAdhesion a = new DemandeAdhesion();
        a.setId(id);
        a.setNumeroReference("DA-1");
        a.setUtilisateur(m);
        a.setOrganisation(o);
        a.setDateDemande(sameDate);
        DemandeAdhesion b = new DemandeAdhesion();
        b.setId(id);
        b.setNumeroReference("DA-1");
        b.setUtilisateur(m);
        b.setOrganisation(o);
        b.setDateDemande(sameDate);
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setNumeroReference("x");
        d.setUtilisateur(newMembre());
        d.setOrganisation(newOrganisation());
        assertThat(d.toString()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("isApprouvee retourne true pour statut APPROUVEE")
    void isApprouvee_approuvee_returnsTrue() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setStatut("APPROUVEE");
        assertThat(d.isApprouvee()).isTrue();
        assertThat(d.isRejetee()).isFalse();
    }

    @Test
    @DisplayName("isRejetee retourne true pour statut REJETEE")
    void isRejetee_rejetee_returnsTrue() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setStatut("REJETEE");
        assertThat(d.isRejetee()).isTrue();
        assertThat(d.isApprouvee()).isFalse();
    }

    @Test
    @DisplayName("genererNumeroReference retourne un code non vide")
    void genererNumeroReference_returnsNonEmpty() {
        String ref = DemandeAdhesion.genererNumeroReference();
        assertThat(ref).isNotNull().isNotEmpty().startsWith("ADH-");
    }

    @Test
    @DisplayName("onCreate: initialise tous les défauts si null")
    void onCreate_setsDefaults() {
        DemandeAdhesion d = new DemandeAdhesion();
        // Les champs @Builder.Default sont déjà initialisés — les forcer à null pour tester les branches true
        d.setDateDemande(null);
        d.setStatut(null);
        d.setCodeDevise(null);
        d.setFraisAdhesion(null);
        d.setMontantPaye(null);
        // numeroReference est null par défaut (pas de @Builder.Default)
        d.onCreate();
        assertThat(d.getDateDemande()).isNotNull();
        assertThat(d.getStatut()).isEqualTo("EN_ATTENTE");
        assertThat(d.getCodeDevise()).isEqualTo("XOF");
        assertThat(d.getFraisAdhesion()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(d.getMontantPaye()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(d.getNumeroReference()).isNotNull().startsWith("ADH-");
    }

    @Test
    @DisplayName("onCreate: ne modifie pas les champs déjà initialisés")
    void onCreate_preservesExistingValues() {
        DemandeAdhesion d = new DemandeAdhesion();
        LocalDateTime specificDate = LocalDateTime.of(2024, 1, 15, 10, 30);
        d.setDateDemande(specificDate);
        d.setStatut("APPROUVEE");
        d.setCodeDevise("EUR");
        d.setFraisAdhesion(BigDecimal.valueOf(100));
        d.setMontantPaye(BigDecimal.valueOf(50));
        d.setNumeroReference("ADH-CUSTOM-001");

        d.onCreate();

        assertThat(d.getDateDemande()).isEqualTo(specificDate);
        assertThat(d.getStatut()).isEqualTo("APPROUVEE");
        assertThat(d.getCodeDevise()).isEqualTo("EUR");
        assertThat(d.getFraisAdhesion()).isEqualByComparingTo(BigDecimal.valueOf(100));
        assertThat(d.getMontantPaye()).isEqualByComparingTo(BigDecimal.valueOf(50));
        assertThat(d.getNumeroReference()).isEqualTo("ADH-CUSTOM-001");
    }

    @Test
    @DisplayName("onCreate: génère une référence si numeroReference est vide")
    void onCreate_emptyReference_generatesNew() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setNumeroReference(""); // vide → condition isEmpty() == true
        d.setStatut("EN_ATTENTE");
        d.setCodeDevise("XOF");
        d.setFraisAdhesion(BigDecimal.ZERO);
        d.setMontantPaye(BigDecimal.ZERO);
        d.setDateDemande(LocalDateTime.now());

        d.onCreate();

        assertThat(d.getNumeroReference()).isNotEmpty().startsWith("ADH-");
    }

    @Test
    @DisplayName("isEnAttente retourne true pour statut EN_ATTENTE")
    void isEnAttente_retournsTrue() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setStatut("EN_ATTENTE");
        assertThat(d.isEnAttente()).isTrue();
        assertThat(d.isApprouvee()).isFalse();
        assertThat(d.isRejetee()).isFalse();
    }

    @Test
    @DisplayName("isPayeeIntegralement retourne true quand montantPaye >= fraisAdhesion")
    void isPayeeIntegralement_paymentComplete_returnsTrue() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setFraisAdhesion(new BigDecimal("5000.00"));
        d.setMontantPaye(new BigDecimal("5000.00"));
        assertThat(d.isPayeeIntegralement()).isTrue();
    }

    @Test
    @DisplayName("isPayeeIntegralement retourne false quand montantPaye < fraisAdhesion")
    void isPayeeIntegralement_paymentIncomplete_returnsFalse() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setFraisAdhesion(new BigDecimal("5000.00"));
        d.setMontantPaye(new BigDecimal("2500.00"));
        assertThat(d.isPayeeIntegralement()).isFalse();
    }

    @Test
    @DisplayName("isPayeeIntegralement retourne false quand fraisAdhesion est null")
    void isPayeeIntegralement_nullFrais_returnsFalse() {
        DemandeAdhesion d = new DemandeAdhesion();
        d.setFraisAdhesion(null);
        d.setMontantPaye(BigDecimal.ZERO);
        assertThat(d.isPayeeIntegralement()).isFalse();
    }
}
