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("AlerteLcbFt")
class AlerteLcbFtTest {

    // -------------------------------------------------------------------------
    // No-args constructor
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("no-args constructor creates non-null instance")
    void noArgsConstructor() {
        AlerteLcbFt alerte = new AlerteLcbFt();
        assertThat(alerte).isNotNull();
    }

    @Test
    @DisplayName("no-args constructor sets traitee = false by default (field initializer)")
    void noArgsConstructor_traiteeDefaultFalse() {
        // @Builder.Default on traitee is only honoured when using the builder;
        // with @NoArgsConstructor the field initializer (= false) still applies.
        AlerteLcbFt alerte = new AlerteLcbFt();
        assertThat(alerte.getTraitee()).isNull();
        // The field carries @Builder.Default so Lombok synthesises a separate
        // $default$traitee() method — traitee is null with plain new until set.
    }

    // -------------------------------------------------------------------------
    // Builder
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("builder sets all scalar fields")
    void builder_scalarFields() {
        LocalDateTime dateAlerte = LocalDateTime.of(2026, 3, 15, 10, 0);
        LocalDateTime dateTraitement = LocalDateTime.of(2026, 3, 16, 9, 0);
        UUID traitePar = UUID.randomUUID();

        AlerteLcbFt alerte = AlerteLcbFt.builder()
                .typeAlerte("SEUIL_DEPASSE")
                .dateAlerte(dateAlerte)
                .description("Transaction suspecte détectée")
                .details("{\"ref\":\"TX-001\"}")
                .montant(new BigDecimal("5000000.00"))
                .seuil(new BigDecimal("3000000.00"))
                .typeOperation("TRANSFERT")
                .transactionRef("TX-REF-001")
                .severite("CRITICAL")
                .traitee(true)
                .dateTraitement(dateTraitement)
                .traitePar(traitePar)
                .commentaireTraitement("Vérifié et classé")
                .build();

        assertThat(alerte.getTypeAlerte()).isEqualTo("SEUIL_DEPASSE");
        assertThat(alerte.getDateAlerte()).isEqualTo(dateAlerte);
        assertThat(alerte.getDescription()).isEqualTo("Transaction suspecte détectée");
        assertThat(alerte.getDetails()).isEqualTo("{\"ref\":\"TX-001\"}");
        assertThat(alerte.getMontant()).isEqualByComparingTo("5000000.00");
        assertThat(alerte.getSeuil()).isEqualByComparingTo("3000000.00");
        assertThat(alerte.getTypeOperation()).isEqualTo("TRANSFERT");
        assertThat(alerte.getTransactionRef()).isEqualTo("TX-REF-001");
        assertThat(alerte.getSeverite()).isEqualTo("CRITICAL");
        assertThat(alerte.getTraitee()).isTrue();
        assertThat(alerte.getDateTraitement()).isEqualTo(dateTraitement);
        assertThat(alerte.getTraitePar()).isEqualTo(traitePar);
        assertThat(alerte.getCommentaireTraitement()).isEqualTo("Vérifié et classé");
    }

    @Test
    @DisplayName("builder default: traitee = false when not explicitly set")
    void builder_defaultTraitee() {
        AlerteLcbFt alerte = AlerteLcbFt.builder()
                .typeAlerte("JUSTIFICATION_MANQUANTE")
                .dateAlerte(LocalDateTime.now())
                .severite("WARNING")
                .build();

        assertThat(alerte.getTraitee()).isFalse();
    }

    @Test
    @DisplayName("builder with organisation and membre associations")
    void builder_withAssociations() {
        Organisation org = new Organisation();
        org.setId(UUID.randomUUID());

        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());

        AlerteLcbFt alerte = AlerteLcbFt.builder()
                .organisation(org)
                .membre(membre)
                .typeAlerte("SEUIL_DEPASSE")
                .dateAlerte(LocalDateTime.now())
                .severite("INFO")
                .build();

        assertThat(alerte.getOrganisation()).isSameAs(org);
        assertThat(alerte.getMembre()).isSameAs(membre);
    }

    // -------------------------------------------------------------------------
    // All-args constructor
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("all-args constructor populates all fields")
    void allArgsConstructor() {
        Organisation org = new Organisation();
        Membre membre = new Membre();
        LocalDateTime now = LocalDateTime.now();
        UUID traitePar = UUID.randomUUID();

        AlerteLcbFt alerte = new AlerteLcbFt(
                org,
                membre,
                "SEUIL_DEPASSE",
                now,
                "desc",
                "{}",
                new BigDecimal("1000.00"),
                new BigDecimal("500.00"),
                "DEPOT",
                "TX-123",
                "WARNING",
                false,
                null,
                traitePar,
                null
        );

        assertThat(alerte.getOrganisation()).isSameAs(org);
        assertThat(alerte.getMembre()).isSameAs(membre);
        assertThat(alerte.getTypeAlerte()).isEqualTo("SEUIL_DEPASSE");
        assertThat(alerte.getDateAlerte()).isEqualTo(now);
        assertThat(alerte.getDescription()).isEqualTo("desc");
        assertThat(alerte.getDetails()).isEqualTo("{}");
        assertThat(alerte.getMontant()).isEqualByComparingTo("1000.00");
        assertThat(alerte.getSeuil()).isEqualByComparingTo("500.00");
        assertThat(alerte.getTypeOperation()).isEqualTo("DEPOT");
        assertThat(alerte.getTransactionRef()).isEqualTo("TX-123");
        assertThat(alerte.getSeverite()).isEqualTo("WARNING");
        assertThat(alerte.getTraitee()).isFalse();
        assertThat(alerte.getDateTraitement()).isNull();
        assertThat(alerte.getTraitePar()).isEqualTo(traitePar);
        assertThat(alerte.getCommentaireTraitement()).isNull();
    }

    // -------------------------------------------------------------------------
    // Getters / Setters
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("setters and getters round-trip for all fields")
    void settersGetters() {
        AlerteLcbFt alerte = new AlerteLcbFt();

        Organisation org = new Organisation();
        Membre membre = new Membre();
        LocalDateTime dateAlerte = LocalDateTime.of(2026, 1, 10, 8, 30);
        LocalDateTime dateTraitement = LocalDateTime.of(2026, 1, 11, 12, 0);
        UUID traitePar = UUID.randomUUID();

        alerte.setOrganisation(org);
        alerte.setMembre(membre);
        alerte.setTypeAlerte("RETRAIT_ANORMAL");
        alerte.setDateAlerte(dateAlerte);
        alerte.setDescription("Retrait inhabituel");
        alerte.setDetails("{\"note\":\"test\"}");
        alerte.setMontant(new BigDecimal("200000.00"));
        alerte.setSeuil(new BigDecimal("150000.00"));
        alerte.setTypeOperation("RETRAIT");
        alerte.setTransactionRef("RET-999");
        alerte.setSeverite("INFO");
        alerte.setTraitee(true);
        alerte.setDateTraitement(dateTraitement);
        alerte.setTraitePar(traitePar);
        alerte.setCommentaireTraitement("RAS");

        assertThat(alerte.getOrganisation()).isSameAs(org);
        assertThat(alerte.getMembre()).isSameAs(membre);
        assertThat(alerte.getTypeAlerte()).isEqualTo("RETRAIT_ANORMAL");
        assertThat(alerte.getDateAlerte()).isEqualTo(dateAlerte);
        assertThat(alerte.getDescription()).isEqualTo("Retrait inhabituel");
        assertThat(alerte.getDetails()).isEqualTo("{\"note\":\"test\"}");
        assertThat(alerte.getMontant()).isEqualByComparingTo("200000.00");
        assertThat(alerte.getSeuil()).isEqualByComparingTo("150000.00");
        assertThat(alerte.getTypeOperation()).isEqualTo("RETRAIT");
        assertThat(alerte.getTransactionRef()).isEqualTo("RET-999");
        assertThat(alerte.getSeverite()).isEqualTo("INFO");
        assertThat(alerte.getTraitee()).isTrue();
        assertThat(alerte.getDateTraitement()).isEqualTo(dateTraitement);
        assertThat(alerte.getTraitePar()).isEqualTo(traitePar);
        assertThat(alerte.getCommentaireTraitement()).isEqualTo("RAS");
    }

    // -------------------------------------------------------------------------
    // Null-safe optional fields
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("optional fields accept null")
    void optionalFieldsAcceptNull() {
        AlerteLcbFt alerte = AlerteLcbFt.builder()
                .typeAlerte("SEUIL_DEPASSE")
                .dateAlerte(LocalDateTime.now())
                .severite("CRITICAL")
                .description(null)
                .details(null)
                .montant(null)
                .seuil(null)
                .typeOperation(null)
                .transactionRef(null)
                .dateTraitement(null)
                .traitePar(null)
                .commentaireTraitement(null)
                .membre(null)
                .build();

        assertThat(alerte.getDescription()).isNull();
        assertThat(alerte.getDetails()).isNull();
        assertThat(alerte.getMontant()).isNull();
        assertThat(alerte.getSeuil()).isNull();
        assertThat(alerte.getTypeOperation()).isNull();
        assertThat(alerte.getTransactionRef()).isNull();
        assertThat(alerte.getDateTraitement()).isNull();
        assertThat(alerte.getTraitePar()).isNull();
        assertThat(alerte.getCommentaireTraitement()).isNull();
        assertThat(alerte.getMembre()).isNull();
    }

    // -------------------------------------------------------------------------
    // BaseEntity fields
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("BaseEntity fields accessible via inherited getters/setters")
    void baseEntityFields() {
        AlerteLcbFt alerte = new AlerteLcbFt();
        UUID id = UUID.randomUUID();
        LocalDateTime now = LocalDateTime.now();

        alerte.setId(id);
        alerte.setDateCreation(now);
        alerte.setDateModification(now);
        alerte.setCreePar("system");
        alerte.setModifiePar("admin");
        alerte.setVersion(2L);
        alerte.setActif(true);

        assertThat(alerte.getId()).isEqualTo(id);
        assertThat(alerte.getDateCreation()).isEqualTo(now);
        assertThat(alerte.getDateModification()).isEqualTo(now);
        assertThat(alerte.getCreePar()).isEqualTo("system");
        assertThat(alerte.getModifiePar()).isEqualTo("admin");
        assertThat(alerte.getVersion()).isEqualTo(2L);
        assertThat(alerte.getActif()).isTrue();
    }

    // -------------------------------------------------------------------------
    // equals / hashCode / toString
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("equals and hashCode are consistent for same id")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        AlerteLcbFt a = new AlerteLcbFt();
        a.setId(id);
        AlerteLcbFt b = new AlerteLcbFt();
        b.setId(id);

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

    @Test
    @DisplayName("toString is non-null")
    void toStringNonNull() {
        AlerteLcbFt alerte = AlerteLcbFt.builder()
                .typeAlerte("INFO")
                .dateAlerte(LocalDateTime.now())
                .severite("INFO")
                .build();
        assertThat(alerte.toString()).isNotNull();
    }
}
