package dev.lions.unionflow.server.entity.mutuelle.parts;

import dev.lions.unionflow.server.api.enums.mutuelle.parts.TypeTransactionPartsSociales;
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("TransactionPartsSociales — entité")
class TransactionPartsSocialesTest {

    // ─── constructeur no-arg ──────────────────────────────────────────────────

    @Test
    @DisplayName("constructeur no-arg : instance créée")
    void noArgConstructor_instanceCreated() {
        TransactionPartsSociales t = new TransactionPartsSociales();
        assertThat(t).isNotNull();
        assertThat(t.getCompte()).isNull();
        assertThat(t.getTypeTransaction()).isNull();
        assertThat(t.getNombreParts()).isNull();
        assertThat(t.getMontant()).isNull();
    }

    // ─── setters / getters ────────────────────────────────────────────────────

    @Test
    @DisplayName("setters et getters — tous les champs")
    void settersGetters_allFields() {
        TransactionPartsSociales t = new TransactionPartsSociales();

        ComptePartsSociales compte = ComptePartsSociales.builder()
                .numeroCompte("CPS-001")
                .valeurNominale(new BigDecimal("5000"))
                .build();
        compte.setId(UUID.randomUUID());

        LocalDateTime dateTransaction = LocalDateTime.of(2026, 4, 20, 10, 30);

        t.setCompte(compte);
        t.setTypeTransaction(TypeTransactionPartsSociales.SOUSCRIPTION);
        t.setNombreParts(5);
        t.setMontant(new BigDecimal("25000"));
        t.setSoldePartsAvant(10);
        t.setSoldePartsApres(15);
        t.setMotif("Souscription initiale");
        t.setReferenceExterne("REF-EXT-001");
        t.setDateTransaction(dateTransaction);

        assertThat(t.getCompte()).isSameAs(compte);
        assertThat(t.getTypeTransaction()).isEqualTo(TypeTransactionPartsSociales.SOUSCRIPTION);
        assertThat(t.getNombreParts()).isEqualTo(5);
        assertThat(t.getMontant()).isEqualByComparingTo("25000");
        assertThat(t.getSoldePartsAvant()).isEqualTo(10);
        assertThat(t.getSoldePartsApres()).isEqualTo(15);
        assertThat(t.getMotif()).isEqualTo("Souscription initiale");
        assertThat(t.getReferenceExterne()).isEqualTo("REF-EXT-001");
        assertThat(t.getDateTransaction()).isEqualTo(dateTransaction);
    }

    // ─── builder ──────────────────────────────────────────────────────────────

    @Test
    @DisplayName("builder : valeurs par défaut (@Builder.Default)")
    void builder_defaults() {
        TransactionPartsSociales t = TransactionPartsSociales.builder()
                .typeTransaction(TypeTransactionPartsSociales.SOUSCRIPTION)
                .nombreParts(1)
                .montant(new BigDecimal("5000"))
                .build();

        assertThat(t.getSoldePartsAvant()).isEqualTo(0);
        assertThat(t.getSoldePartsApres()).isEqualTo(0);
        assertThat(t.getDateTransaction()).isNotNull();
        // dateTransaction initialized to LocalDateTime.now() by @Builder.Default
        assertThat(t.getDateTransaction()).isBeforeOrEqualTo(LocalDateTime.now());
    }

    @Test
    @DisplayName("builder : valeurs personnalisées")
    void builder_customValues() {
        ComptePartsSociales compte = ComptePartsSociales.builder()
                .numeroCompte("CPS-002")
                .valeurNominale(new BigDecimal("5000"))
                .build();
        LocalDateTime now = LocalDateTime.now();

        TransactionPartsSociales t = TransactionPartsSociales.builder()
                .compte(compte)
                .typeTransaction(TypeTransactionPartsSociales.CESSION_PARTIELLE)
                .nombreParts(3)
                .montant(new BigDecimal("15000"))
                .soldePartsAvant(10)
                .soldePartsApres(7)
                .motif("Cession à titre onéreux")
                .referenceExterne("REF-CESS-001")
                .dateTransaction(now)
                .build();

        assertThat(t.getCompte()).isSameAs(compte);
        assertThat(t.getTypeTransaction()).isEqualTo(TypeTransactionPartsSociales.CESSION_PARTIELLE);
        assertThat(t.getNombreParts()).isEqualTo(3);
        assertThat(t.getMontant()).isEqualByComparingTo("15000");
        assertThat(t.getSoldePartsAvant()).isEqualTo(10);
        assertThat(t.getSoldePartsApres()).isEqualTo(7);
        assertThat(t.getMotif()).isEqualTo("Cession à titre onéreux");
        assertThat(t.getReferenceExterne()).isEqualTo("REF-CESS-001");
        assertThat(t.getDateTransaction()).isEqualTo(now);
    }

    // ─── AllArgsConstructor ───────────────────────────────────────────────────

    @Test
    @DisplayName("AllArgsConstructor : instanciation complète")
    void allArgsConstructor() {
        ComptePartsSociales compte = ComptePartsSociales.builder()
                .numeroCompte("CPS-ALL")
                .valeurNominale(new BigDecimal("5000"))
                .build();
        TypeTransactionPartsSociales type = TypeTransactionPartsSociales.RACHAT_TOTAL;
        Integer nombreParts = 20;
        BigDecimal montant = new BigDecimal("100000");
        Integer soldeAvant = 20;
        Integer soldeApres = 0;
        String motif = "Rachat complet";
        String refExterne = "REF-RACHAT-001";
        LocalDateTime dateTransaction = LocalDateTime.of(2026, 4, 20, 12, 0);

        TransactionPartsSociales t = new TransactionPartsSociales(
                compte, type, nombreParts, montant,
                soldeAvant, soldeApres, motif, refExterne, dateTransaction);

        assertThat(t.getCompte()).isSameAs(compte);
        assertThat(t.getTypeTransaction()).isEqualTo(TypeTransactionPartsSociales.RACHAT_TOTAL);
        assertThat(t.getNombreParts()).isEqualTo(20);
        assertThat(t.getMontant()).isEqualByComparingTo("100000");
        assertThat(t.getSoldePartsAvant()).isEqualTo(20);
        assertThat(t.getSoldePartsApres()).isEqualTo(0);
        assertThat(t.getMotif()).isEqualTo("Rachat complet");
        assertThat(t.getReferenceExterne()).isEqualTo("REF-RACHAT-001");
        assertThat(t.getDateTransaction()).isEqualTo(dateTransaction);
    }

    // ─── TypeTransactionPartsSociales enum ───────────────────────────────────

    @Test
    @DisplayName("TypeTransactionPartsSociales : toutes les valeurs accessibles avec libellé")
    void typeTransactionEnum_allValues() {
        assertThat(TypeTransactionPartsSociales.values()).hasSize(6);
        assertThat(TypeTransactionPartsSociales.SOUSCRIPTION.getLibelle())
                .isEqualTo("Souscription de parts sociales");
        assertThat(TypeTransactionPartsSociales.SOUSCRIPTION_IMPORT.getLibelle())
                .contains("Import");
        assertThat(TypeTransactionPartsSociales.CESSION_PARTIELLE.getLibelle())
                .contains("Cession");
        assertThat(TypeTransactionPartsSociales.RACHAT_TOTAL.getLibelle())
                .contains("Rachat");
        assertThat(TypeTransactionPartsSociales.PAIEMENT_DIVIDENDE.getLibelle())
                .contains("dividende");
        assertThat(TypeTransactionPartsSociales.CORRECTION.getLibelle())
                .contains("Correction");
    }

    @Test
    @DisplayName("TypeTransactionPartsSociales : valueOf fonctionne")
    void typeTransactionEnum_valueOf() {
        assertThat(TypeTransactionPartsSociales.valueOf("SOUSCRIPTION"))
                .isEqualTo(TypeTransactionPartsSociales.SOUSCRIPTION);
        assertThat(TypeTransactionPartsSociales.valueOf("PAIEMENT_DIVIDENDE"))
                .isEqualTo(TypeTransactionPartsSociales.PAIEMENT_DIVIDENDE);
    }

    // ─── equals / hashCode / toString ─────────────────────────────────────────

    @Test
    @DisplayName("equals : même id → égaux")
    void equals_sameId() {
        UUID id = UUID.randomUUID();
        TransactionPartsSociales a = TransactionPartsSociales.builder()
                .typeTransaction(TypeTransactionPartsSociales.SOUSCRIPTION)
                .nombreParts(1)
                .montant(new BigDecimal("5000"))
                .build();
        a.setId(id);

        TransactionPartsSociales b = TransactionPartsSociales.builder()
                .typeTransaction(TypeTransactionPartsSociales.SOUSCRIPTION)
                .nombreParts(1)
                .montant(new BigDecimal("5000"))
                .build();
        b.setId(id);

        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("equals : id différents → non égaux")
    void equals_differentId() {
        TransactionPartsSociales a = TransactionPartsSociales.builder()
                .typeTransaction(TypeTransactionPartsSociales.SOUSCRIPTION)
                .nombreParts(1)
                .montant(new BigDecimal("5000"))
                .build();
        a.setId(UUID.randomUUID());

        TransactionPartsSociales b = TransactionPartsSociales.builder()
                .typeTransaction(TypeTransactionPartsSociales.SOUSCRIPTION)
                .nombreParts(1)
                .montant(new BigDecimal("5000"))
                .build();
        b.setId(UUID.randomUUID());

        assertThat(a).isNotEqualTo(b);
    }

    @Test
    @DisplayName("toString : non null et non vide")
    void toString_notNull() {
        TransactionPartsSociales t = TransactionPartsSociales.builder()
                .typeTransaction(TypeTransactionPartsSociales.CORRECTION)
                .nombreParts(1)
                .montant(BigDecimal.ZERO)
                .build();
        assertThat(t.toString()).isNotNull().isNotEmpty();
    }

    // ─── BaseEntity ───────────────────────────────────────────────────────────

    @Test
    @DisplayName("BaseEntity : id, actif, audit fields accessibles")
    void baseEntity_fields() {
        TransactionPartsSociales t = new TransactionPartsSociales();
        UUID id = UUID.randomUUID();
        t.setId(id);
        t.setActif(true);
        t.setVersion(3L);

        assertThat(t.getId()).isEqualTo(id);
        assertThat(t.getActif()).isTrue();
        assertThat(t.getVersion()).isEqualTo(3L);
    }

    @Test
    @DisplayName("marquerCommeModifie : met à jour modifiePar et dateModification")
    void marquerCommeModifie_updatesFields() {
        TransactionPartsSociales t = new TransactionPartsSociales();
        t.marquerCommeModifie("comptable@test.com");

        assertThat(t.getModifiePar()).isEqualTo("comptable@test.com");
        assertThat(t.getDateModification()).isNotNull();
    }
}
