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.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("Cotisation")
class CotisationTest {

    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() {
        Cotisation c = new Cotisation();
        c.setNumeroReference("COT-2025-00000001");
        c.setMembre(newMembre());
        c.setOrganisation(newOrganisation());
        c.setTypeCotisation("MENSUEL");
        c.setLibelle("Cotisation janvier");
        c.setMontantDu(new BigDecimal("5000.00"));
        c.setMontantPaye(new BigDecimal("2000.00"));
        c.setCodeDevise("XOF");
        c.setStatut("EN_ATTENTE");
        c.setDateEcheance(LocalDate.now().plusMonths(1));
        c.setAnnee(2025);
        c.setMois(1);
        c.setRecurrente(true);

        assertThat(c.getNumeroReference()).isEqualTo("COT-2025-00000001");
        assertThat(c.getTypeCotisation()).isEqualTo("MENSUEL");
        assertThat(c.getLibelle()).isEqualTo("Cotisation janvier");
        assertThat(c.getMontantDu()).isEqualByComparingTo("5000.00");
        assertThat(c.getMontantPaye()).isEqualByComparingTo("2000.00");
        assertThat(c.getCodeDevise()).isEqualTo("XOF");
        assertThat(c.getStatut()).isEqualTo("EN_ATTENTE");
        assertThat(c.getAnnee()).isEqualTo(2025);
        assertThat(c.getMois()).isEqualTo(1);
        assertThat(c.getRecurrente()).isTrue();
    }

    @Test
    @DisplayName("getMontantRestant")
    void getMontantRestant() {
        Cotisation c = new Cotisation();
        c.setMontantDu(new BigDecimal("100.00"));
        c.setMontantPaye(new BigDecimal("30.00"));
        assertThat(c.getMontantRestant()).isEqualByComparingTo("70.00");
        c.setMontantPaye(null);
        assertThat(c.getMontantRestant()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("isEntierementPayee")
    void isEntierementPayee() {
        Cotisation c = new Cotisation();
        c.setMontantDu(new BigDecimal("100.00"));
        c.setMontantPaye(new BigDecimal("100.00"));
        assertThat(c.isEntierementPayee()).isTrue();
        c.setMontantPaye(new BigDecimal("50.00"));
        assertThat(c.isEntierementPayee()).isFalse();
    }

    @Test
    @DisplayName("isEnRetard: true si échéance passée et non payée")
    void isEnRetard() {
        Cotisation c = new Cotisation();
        c.setDateEcheance(LocalDate.now().minusDays(1));
        c.setMontantDu(new BigDecimal("100.00"));
        c.setMontantPaye(BigDecimal.ZERO);
        assertThat(c.isEnRetard()).isTrue();
        c.setMontantPaye(new BigDecimal("100.00"));
        assertThat(c.isEnRetard()).isFalse();
    }

    @Test
    @DisplayName("genererNumeroReference non null")
    void genererNumeroReference() {
        String ref = Cotisation.genererNumeroReference();
        assertThat(ref).startsWith("COT-").isNotNull();
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        Cotisation a = new Cotisation();
        a.setId(id);
        a.setNumeroReference("REF-1");
        a.setMembre(newMembre());
        a.setOrganisation(newOrganisation());
        a.setTypeCotisation("MENSUEL");
        a.setLibelle("L");
        a.setMontantDu(BigDecimal.ONE);
        a.setCodeDevise("XOF");
        a.setStatut("EN_ATTENTE");
        a.setDateEcheance(LocalDate.now());
        a.setAnnee(2025);
        Cotisation b = new Cotisation();
        b.setId(id);
        b.setNumeroReference("REF-1");
        b.setMembre(a.getMembre());
        b.setOrganisation(a.getOrganisation());
        b.setTypeCotisation("MENSUEL");
        b.setLibelle("L");
        b.setMontantDu(BigDecimal.ONE);
        b.setCodeDevise("XOF");
        b.setStatut("EN_ATTENTE");
        b.setDateEcheance(LocalDate.now());
        b.setAnnee(2025);
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        Cotisation c = new Cotisation();
        c.setNumeroReference("REF");
        c.setTypeCotisation("MENSUEL");
        c.setLibelle("L");
        c.setMontantDu(BigDecimal.ONE);
        c.setCodeDevise("XOF");
        c.setStatut("EN_ATTENTE");
        c.setDateEcheance(LocalDate.now());
        c.setAnnee(2025);
        assertThat(c.toString()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("getMontantRestant: ZERO si montantDu null")
    void getMontantRestant_montantDuNull_returnsZero() {
        Cotisation c = new Cotisation();
        c.setMontantPaye(new BigDecimal("50.00"));
        // montantDu = null
        assertThat(c.getMontantRestant()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("isEnRetard: false si dateEcheance null")
    void isEnRetard_echeanceNull_returnsFalse() {
        Cotisation c = new Cotisation();
        c.setMontantDu(new BigDecimal("100.00"));
        c.setMontantPaye(BigDecimal.ZERO);
        // dateEcheance = null
        assertThat(c.isEnRetard()).isFalse();
    }

    @Test
    @DisplayName("onCreate: numeroReference déjà défini → conservé (branche false)")
    void onCreate_numeroReferenceDejaDefini_conserve() {
        // numeroReference déjà défini → non écrasé par onCreate
        Cotisation c = new Cotisation();
        c.setNumeroReference("COT-EXISTANT-001");
        c.setCodeDevise("XOF");
        c.setStatut("EN_ATTENTE");
        c.setMontantPaye(BigDecimal.ZERO);
        c.setNombreRappels(0);
        c.setRecurrente(false);

        c.onCreate();

        // numeroReference doit rester inchangé
        assertThat(c.getNumeroReference()).isEqualTo("COT-EXISTANT-001");
    }

    @Test
    @DisplayName("onCreate: numeroReference vide (empty string) → généré (branche isEmpty)")
    void onCreate_emptyNumeroReference_generated() throws Exception {
        Cotisation c = new Cotisation();
        c.setNumeroReference(""); // non null mais vide → isEmpty() est true → doit générer
        c.setCodeDevise("XOF");
        c.setStatut("EN_ATTENTE");
        c.setMontantPaye(BigDecimal.ZERO);
        c.setNombreRappels(0);
        c.setRecurrente(false);

        java.lang.reflect.Method onCreate = Cotisation.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(c);

        assertThat(c.getNumeroReference()).isNotEmpty().startsWith("COT-");
    }

    @Test
    @DisplayName("onCreate: initialise les défauts si null")
    void onCreate_setsDefaults() {
        Cotisation c = new Cotisation();
        // Force null pour couvrir toutes les branches de initialisation
        c.setNumeroReference(null);
        c.setCodeDevise(null);
        c.setStatut(null);
        c.setMontantPaye(null);
        c.setNombreRappels(null);
        c.setRecurrente(null);
        c.onCreate();
        assertThat(c.getNumeroReference()).isNotNull().startsWith("COT-");
        assertThat(c.getCodeDevise()).isEqualTo("XOF");
        assertThat(c.getStatut()).isEqualTo("EN_ATTENTE");
        assertThat(c.getMontantPaye()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(c.getNombreRappels()).isEqualTo(0);
        assertThat(c.getRecurrente()).isFalse();
    }
}
