package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@DisplayName("LigneEcriture")
class LigneEcritureTest {

    private static EcritureComptable newEcriture() {
        JournalComptable j = new JournalComptable();
        j.setId(UUID.randomUUID());
        j.setCode("BQ");
        j.setLibelle("B");
        j.setTypeJournal(TypeJournalComptable.BANQUE);
        EcritureComptable e = new EcritureComptable();
        e.setId(UUID.randomUUID());
        e.setNumeroPiece("ECR-1");
        e.setDateEcriture(java.time.LocalDate.now());
        e.setLibelle("L");
        e.setJournal(j);
        return e;
    }

    private static CompteComptable newCompte() {
        CompteComptable c = new CompteComptable();
        c.setId(UUID.randomUUID());
        c.setNumeroCompte("512000");
        c.setLibelle("Banque");
        c.setTypeCompte(TypeCompteComptable.TRESORERIE);
        c.setClasseComptable(5);
        return c;
    }

    @Test
    @DisplayName("getters/setters")
    void gettersSetters() {
        LigneEcriture l = new LigneEcriture();
        l.setNumeroLigne(1);
        l.setMontantDebit(new BigDecimal("100.00"));
        l.setMontantCredit(BigDecimal.ZERO);
        l.setLibelle("Ligne 1");
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());

        assertThat(l.getNumeroLigne()).isEqualTo(1);
        assertThat(l.getMontantDebit()).isEqualByComparingTo("100.00");
        assertThat(l.getMontantCredit()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(l.getLibelle()).isEqualTo("Ligne 1");
    }

    @Test
    @DisplayName("isValide: true si débit XOR crédit")
    void isValide() {
        LigneEcriture l = new LigneEcriture();
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        l.setNumeroLigne(1);
        l.setMontantDebit(new BigDecimal("100"));
        l.setMontantCredit(BigDecimal.ZERO);
        assertThat(l.isValide()).isTrue();
        l.setMontantDebit(BigDecimal.ZERO);
        l.setMontantCredit(new BigDecimal("100"));
        assertThat(l.isValide()).isTrue();
        l.setMontantDebit(new BigDecimal("50"));
        l.setMontantCredit(new BigDecimal("50"));
        assertThat(l.isValide()).isFalse();
    }

    @Test
    @DisplayName("isValide: false si ni débit ni crédit (both null)")
    void isValide_false_bothNull() {
        LigneEcriture l = new LigneEcriture();
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        l.setNumeroLigne(1);
        l.setMontantDebit(null);
        l.setMontantCredit(null);
        assertThat(l.isValide()).isFalse();
    }

    @Test
    @DisplayName("isValide: false si ni débit ni crédit (both zero)")
    void isValide_false_bothZero() {
        LigneEcriture l = new LigneEcriture();
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        l.setNumeroLigne(1);
        l.setMontantDebit(BigDecimal.ZERO);
        l.setMontantCredit(BigDecimal.ZERO);
        assertThat(l.isValide()).isFalse();
    }

    @Test
    @DisplayName("getMontant: débit ou crédit")
    void getMontant() {
        LigneEcriture l = new LigneEcriture();
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        l.setNumeroLigne(1);
        l.setMontantDebit(new BigDecimal("100"));
        l.setMontantCredit(BigDecimal.ZERO);
        assertThat(l.getMontant()).isEqualByComparingTo("100");
        l.setMontantDebit(BigDecimal.ZERO);
        l.setMontantCredit(new BigDecimal("200"));
        assertThat(l.getMontant()).isEqualByComparingTo("200");
    }

    @Test
    @DisplayName("getMontant: retourne ZERO si ni débit ni crédit (both null)")
    void getMontant_zero_whenBothNull() {
        LigneEcriture l = new LigneEcriture();
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        l.setNumeroLigne(1);
        l.setMontantDebit(null);
        l.setMontantCredit(null);
        assertThat(l.getMontant()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        EcritureComptable e = newEcriture();
        CompteComptable c = newCompte();
        LigneEcriture a = new LigneEcriture();
        a.setId(id);
        a.setNumeroLigne(1);
        a.setEcriture(e);
        a.setCompteComptable(c);
        LigneEcriture b = new LigneEcriture();
        b.setId(id);
        b.setNumeroLigne(1);
        b.setEcriture(e);
        b.setCompteComptable(c);
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        LigneEcriture l = new LigneEcriture();
        l.setNumeroLigne(1);
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        assertThat(l.toString()).isNotNull().isNotEmpty();
    }

    // ── Branch coverage manquantes ─────────────────────────────────────────

    /**
     * L82 branch manquante : montantCredit != null mais == 0 (not > 0)
     * → `montantCredit != null && montantCredit.compareTo(BigDecimal.ZERO) > 0` → false
     * → retourne BigDecimal.ZERO (la dernière ligne)
     */
    @Test
    @DisplayName("getMontant: montantDebit null, montantCredit = ZERO → retourne ZERO (branche false compareTo)")
    void getMontant_debitNull_creditZero_returnsZero() {
        LigneEcriture l = new LigneEcriture();
        l.setEcriture(newEcriture());
        l.setCompteComptable(newCompte());
        l.setNumeroLigne(1);
        // montantDebit == null → premiere condition false
        // montantCredit != null mais == 0 → deuxième condition: compareTo > 0 est FALSE
        // → retourne BigDecimal.ZERO
        l.setMontantDebit(null);
        l.setMontantCredit(BigDecimal.ZERO);
        assertThat(l.getMontant()).isEqualByComparingTo(BigDecimal.ZERO);
    }
}
