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

import dev.lions.unionflow.server.api.enums.mutuelle.parts.StatutComptePartsSociales;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@DisplayName("ComptePartsSociales — entité")
class ComptePartsSocialesTest {

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

    @Test
    @DisplayName("constructeur no-arg : instance créée, champs null sans @Builder.Default")
    void noArgConstructor_instanceCreated() {
        ComptePartsSociales c = new ComptePartsSociales();
        assertThat(c).isNotNull();
        assertThat(c.getMembre()).isNull();
        assertThat(c.getOrganisation()).isNull();
        assertThat(c.getNumeroCompte()).isNull();
    }

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

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

        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        Organisation org = new Organisation();
        org.setId(UUID.randomUUID());
        LocalDate ouverture = LocalDate.of(2025, 1, 15);
        LocalDate derniereOp = LocalDate.of(2026, 4, 1);

        c.setMembre(membre);
        c.setOrganisation(org);
        c.setNumeroCompte("CPS-2025-001");
        c.setNombreParts(10);
        c.setValeurNominale(new BigDecimal("5000"));
        c.setMontantTotal(new BigDecimal("50000"));
        c.setTotalDividendesRecus(new BigDecimal("2500"));
        c.setStatut(StatutComptePartsSociales.ACTIF);
        c.setDateOuverture(ouverture);
        c.setDateDerniereOperation(derniereOp);
        c.setNotes("Note de test");

        assertThat(c.getMembre()).isSameAs(membre);
        assertThat(c.getOrganisation()).isSameAs(org);
        assertThat(c.getNumeroCompte()).isEqualTo("CPS-2025-001");
        assertThat(c.getNombreParts()).isEqualTo(10);
        assertThat(c.getValeurNominale()).isEqualByComparingTo("5000");
        assertThat(c.getMontantTotal()).isEqualByComparingTo("50000");
        assertThat(c.getTotalDividendesRecus()).isEqualByComparingTo("2500");
        assertThat(c.getStatut()).isEqualTo(StatutComptePartsSociales.ACTIF);
        assertThat(c.getDateOuverture()).isEqualTo(ouverture);
        assertThat(c.getDateDerniereOperation()).isEqualTo(derniereOp);
        assertThat(c.getNotes()).isEqualTo("Note de test");
    }

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

    @Test
    @DisplayName("builder : valeurs par défaut (@Builder.Default)")
    void builder_defaults() {
        ComptePartsSociales c = ComptePartsSociales.builder()
                .numeroCompte("CPS-TEST-001")
                .valeurNominale(new BigDecimal("5000"))
                .build();

        assertThat(c.getNombreParts()).isEqualTo(0);
        assertThat(c.getMontantTotal()).isEqualByComparingTo("0");
        assertThat(c.getTotalDividendesRecus()).isEqualByComparingTo("0");
        assertThat(c.getStatut()).isEqualTo(StatutComptePartsSociales.ACTIF);
        assertThat(c.getDateOuverture()).isEqualTo(LocalDate.now());
    }

    @Test
    @DisplayName("builder : valeurs personnalisées")
    void builder_customValues() {
        Membre membre = new Membre();
        Organisation org = new Organisation();
        LocalDate ouverture = LocalDate.of(2024, 6, 1);

        ComptePartsSociales c = ComptePartsSociales.builder()
                .membre(membre)
                .organisation(org)
                .numeroCompte("CPS-2024-042")
                .nombreParts(25)
                .valeurNominale(new BigDecimal("5000"))
                .montantTotal(new BigDecimal("125000"))
                .totalDividendesRecus(new BigDecimal("6250"))
                .statut(StatutComptePartsSociales.SUSPENDU)
                .dateOuverture(ouverture)
                .notes("Compte suspendu pour régularisation")
                .build();

        assertThat(c.getMembre()).isSameAs(membre);
        assertThat(c.getOrganisation()).isSameAs(org);
        assertThat(c.getNumeroCompte()).isEqualTo("CPS-2024-042");
        assertThat(c.getNombreParts()).isEqualTo(25);
        assertThat(c.getValeurNominale()).isEqualByComparingTo("5000");
        assertThat(c.getMontantTotal()).isEqualByComparingTo("125000");
        assertThat(c.getTotalDividendesRecus()).isEqualByComparingTo("6250");
        assertThat(c.getStatut()).isEqualTo(StatutComptePartsSociales.SUSPENDU);
        assertThat(c.getDateOuverture()).isEqualTo(ouverture);
        assertThat(c.getNotes()).isEqualTo("Compte suspendu pour régularisation");
    }

    // ─── StatutComptePartsSociales enum ──────────────────────────────────────

    @Test
    @DisplayName("StatutComptePartsSociales : toutes les valeurs accessibles")
    void statutEnum_allValues() {
        assertThat(StatutComptePartsSociales.values()).hasSize(3);
        assertThat(StatutComptePartsSociales.ACTIF.getLibelle()).isEqualTo("Compte actif");
        assertThat(StatutComptePartsSociales.SUSPENDU.getLibelle()).isEqualTo("Compte suspendu");
        assertThat(StatutComptePartsSociales.CLOS.getLibelle()).contains("Compte cl");
    }

    @Test
    @DisplayName("StatutComptePartsSociales : valueOf fonctionne")
    void statutEnum_valueOf() {
        assertThat(StatutComptePartsSociales.valueOf("ACTIF")).isEqualTo(StatutComptePartsSociales.ACTIF);
        assertThat(StatutComptePartsSociales.valueOf("CLOS")).isEqualTo(StatutComptePartsSociales.CLOS);
    }

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

    @Test
    @DisplayName("AllArgsConstructor : instanciation complète")
    void allArgsConstructor() {
        Membre membre = new Membre();
        Organisation org = new Organisation();
        String numero = "CPS-ALL-001";
        Integer nombreParts = 5;
        BigDecimal valeurNominale = new BigDecimal("5000");
        BigDecimal montantTotal = new BigDecimal("25000");
        BigDecimal totalDividendes = new BigDecimal("1250");
        StatutComptePartsSociales statut = StatutComptePartsSociales.ACTIF;
        LocalDate dateOuverture = LocalDate.of(2025, 3, 1);
        LocalDate dateDerniereOperation = LocalDate.of(2026, 4, 1);
        String notes = "Test all args";

        ComptePartsSociales c = new ComptePartsSociales(
                membre, org, numero, nombreParts, valeurNominale,
                montantTotal, totalDividendes, statut,
                dateOuverture, dateDerniereOperation, notes);

        assertThat(c.getMembre()).isSameAs(membre);
        assertThat(c.getOrganisation()).isSameAs(org);
        assertThat(c.getNumeroCompte()).isEqualTo("CPS-ALL-001");
        assertThat(c.getNombreParts()).isEqualTo(5);
        assertThat(c.getStatut()).isEqualTo(StatutComptePartsSociales.ACTIF);
    }

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

    @Test
    @DisplayName("equals : même id → égaux")
    void equals_sameId() {
        UUID id = UUID.randomUUID();
        ComptePartsSociales a = ComptePartsSociales.builder()
                .numeroCompte("CPS-A")
                .valeurNominale(new BigDecimal("5000"))
                .build();
        a.setId(id);

        ComptePartsSociales b = ComptePartsSociales.builder()
                .numeroCompte("CPS-A")
                .valeurNominale(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() {
        ComptePartsSociales a = ComptePartsSociales.builder()
                .numeroCompte("CPS-A")
                .valeurNominale(new BigDecimal("5000"))
                .build();
        a.setId(UUID.randomUUID());

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

        assertThat(a).isNotEqualTo(b);
    }

    @Test
    @DisplayName("toString : non null, non vide")
    void toString_notNull() {
        ComptePartsSociales c = ComptePartsSociales.builder()
                .numeroCompte("CPS-STR")
                .valeurNominale(new BigDecimal("5000"))
                .build();
        assertThat(c.toString()).isNotNull().isNotEmpty();
    }

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

    @Test
    @DisplayName("BaseEntity : id, actif, audit fields accessibles")
    void baseEntity_fields() {
        ComptePartsSociales c = new ComptePartsSociales();
        UUID id = UUID.randomUUID();
        c.setId(id);
        c.setActif(false);
        c.setCreePar("createur@test.com");

        assertThat(c.getId()).isEqualTo(id);
        assertThat(c.getActif()).isFalse();
        assertThat(c.getCreePar()).isEqualTo("createur@test.com");
    }
}
