package dev.lions.unionflow.server.api.dto.paiement;

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

import java.math.BigDecimal;
import java.time.LocalDateTime;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

/**
 * Tests unitaires pour WaveBalanceDTO.
 * Couvre toutes les branches : calculerSoldeTotal (via setters), isWalletActif,
 * isSoldeSuffisant, getSoldeDisponibleAujourdhui, mettreAJourApresTransaction.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-21
 */
@DisplayName("Tests WaveBalanceDTO")
class WaveBalanceDTOTest {

  @Nested
  @DisplayName("Constructeurs")
  class Constructeurs {

    @Test
    @DisplayName("constructeur par défaut initialise devise et statut")
    void testConstructeurVide() {
      WaveBalanceDTO dto = new WaveBalanceDTO();
      assertThat(dto.getDevise()).isEqualTo("XOF");
      assertThat(dto.getStatutWallet()).isEqualTo("ACTIVE");
      assertThat(dto.getSoldeEnAttente()).isEqualTo(BigDecimal.ZERO);
      assertThat(dto.getNombreTransactionsAujourdhui()).isEqualTo(0);
    }

    @Test
    @DisplayName("constructeur avec wallet et solde calcule soldeTotal")
    void testConstructeurAvecParams() {
      WaveBalanceDTO dto = new WaveBalanceDTO("wallet-1", BigDecimal.valueOf(1000));
      assertThat(dto.getNumeroWallet()).isEqualTo("wallet-1");
      assertThat(dto.getSoldeDisponible()).isEqualByComparingTo(BigDecimal.valueOf(1000));
      assertThat(dto.getSoldeTotal()).isEqualByComparingTo(BigDecimal.valueOf(1000));
    }
  }

  @Nested
  @DisplayName("setSoldeDisponible / setSoldeEnAttente (calculerSoldeTotal)")
  class CalculerSoldeTotal {

    @Test
    @DisplayName("soldeTotal = disponible + enAttente quand les deux sont définis")
    void testCalculSoldeTotal() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(100));
      dto.setSoldeEnAttente(BigDecimal.valueOf(50));
      assertThat(dto.getSoldeTotal()).isEqualByComparingTo(BigDecimal.valueOf(150));
    }

    @Test
    @DisplayName("setSoldeDisponible recalcule soldeTotal")
    void testSetSoldeDisponible() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(100));
      dto.setSoldeEnAttente(BigDecimal.valueOf(20));
      dto.setSoldeDisponible(BigDecimal.valueOf(200));
      assertThat(dto.getSoldeTotal()).isEqualByComparingTo(BigDecimal.valueOf(220));
    }
  }

  @Nested
  @DisplayName("isWalletActif")
  class IsWalletActif {

    @Test
    @DisplayName("true quand statut ACTIVE")
    void testActif() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.ZERO);
      dto.setStatutWallet("ACTIVE");
      assertThat(dto.isWalletActif()).isTrue();
    }

    @Test
    @DisplayName("false quand statut INACTIVE")
    void testInactif() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.ZERO);
      dto.setStatutWallet("INACTIVE");
      assertThat(dto.isWalletActif()).isFalse();
    }
  }

  @Nested
  @DisplayName("isSoldeSuffisant")
  class IsSoldeSuffisant {

    @Test
    @DisplayName("false quand soldeDisponible null")
    void testSoldeNull() {
      WaveBalanceDTO dto = new WaveBalanceDTO();
      dto.setSoldeDisponible(null);
      assertThat(dto.isSoldeSuffisant(BigDecimal.ONE)).isFalse();
    }

    @Test
    @DisplayName("true quand solde >= montant")
    void testSuffisant() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(100));
      assertThat(dto.isSoldeSuffisant(BigDecimal.valueOf(50))).isTrue();
      assertThat(dto.isSoldeSuffisant(BigDecimal.valueOf(100))).isTrue();
    }

    @Test
    @DisplayName("false quand solde < montant")
    void testInsuffisant() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(100));
      assertThat(dto.isSoldeSuffisant(BigDecimal.valueOf(101))).isFalse();
    }
  }

  @Nested
  @DisplayName("getSoldeDisponibleAujourdhui")
  class GetSoldeDisponibleAujourdhui {

    @Test
    @DisplayName("retourne soldeDisponible quand limiteQuotidienne null")
    void testLimiteNull() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(500));
      dto.setLimiteQuotidienne(null);
      dto.setMontantUtiliseAujourdhui(BigDecimal.ZERO);
      assertThat(dto.getSoldeDisponibleAujourdhui()).isEqualByComparingTo(BigDecimal.valueOf(500));
    }

    @Test
    @DisplayName("retourne soldeDisponible quand montantUtiliseAujourdhui null")
    void testMontantUtiliseNull() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(500));
      dto.setLimiteQuotidienne(BigDecimal.valueOf(1000));
      dto.setMontantUtiliseAujourdhui(null);
      assertThat(dto.getSoldeDisponibleAujourdhui()).isEqualByComparingTo(BigDecimal.valueOf(500));
    }

    @Test
    @DisplayName("retourne le minimum entre solde et limite restante")
    void testMinimum() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(500));
      dto.setLimiteQuotidienne(BigDecimal.valueOf(1000));
      dto.setMontantUtiliseAujourdhui(BigDecimal.valueOf(700));
      assertThat(dto.getSoldeDisponibleAujourdhui()).isEqualByComparingTo(BigDecimal.valueOf(300));
    }
  }

  @Nested
  @DisplayName("mettreAJourApresTransaction")
  class MettreAJourApresTransaction {

    @Test
    @DisplayName("initialise les champs null puis incrémente")
    void testAvecChampsNull() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(1000));
      dto.setMontantUtiliseAujourdhui(null);
      dto.setMontantUtiliseCeMois(null);
      dto.setNombreTransactionsAujourdhui(null);
      dto.setNombreTransactionsCeMois(null);
      dto.mettreAJourApresTransaction(BigDecimal.valueOf(50));
      assertThat(dto.getMontantUtiliseAujourdhui()).isEqualByComparingTo(BigDecimal.valueOf(50));
      assertThat(dto.getMontantUtiliseCeMois()).isEqualByComparingTo(BigDecimal.valueOf(50));
      assertThat(dto.getNombreTransactionsAujourdhui()).isEqualTo(1);
      assertThat(dto.getNombreTransactionsCeMois()).isEqualTo(1);
      assertThat(dto.getDateDerniereMiseAJour()).isNotNull();
    }

    @Test
    @DisplayName("ajoute au montant existant")
    void testAvecValeursExistantes() {
      WaveBalanceDTO dto = new WaveBalanceDTO("w", BigDecimal.valueOf(1000));
      dto.setMontantUtiliseAujourdhui(BigDecimal.valueOf(100));
      dto.setMontantUtiliseCeMois(BigDecimal.valueOf(200));
      dto.setNombreTransactionsAujourdhui(2);
      dto.setNombreTransactionsCeMois(3);
      dto.mettreAJourApresTransaction(BigDecimal.valueOf(25));
      assertThat(dto.getMontantUtiliseAujourdhui()).isEqualByComparingTo(BigDecimal.valueOf(125));
      assertThat(dto.getMontantUtiliseCeMois()).isEqualByComparingTo(BigDecimal.valueOf(225));
      assertThat(dto.getNombreTransactionsAujourdhui()).isEqualTo(3);
      assertThat(dto.getNombreTransactionsCeMois()).isEqualTo(4);
    }
  }

  @Nested
  @DisplayName("toString")
  class ToString {

    @Test
    @DisplayName("contient numeroWallet et solde")
    void testToString() {
      WaveBalanceDTO dto = new WaveBalanceDTO("wallet-1", BigDecimal.valueOf(500));
      String s = dto.toString();
      assertThat(s).contains("wallet-1");
      assertThat(s).contains("500");
    }
  }
}
