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

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

import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EnumSource;

/**
 * Tests unitaires pour DemandeAideDTO
 *
 * @author UnionFlow Team
 * @version 2.0
 * @since 2025-01-16
 */
@DisplayName("Tests DemandeAideDTO")
class DemandeAideDTOTest {

  private DemandeAideDTO demandeAide;

  @BeforeEach
  void setUp() {
    demandeAide = new DemandeAideDTO();
  }

  @Test
  @DisplayName("Test création et getters/setters de base")
  void testCreationEtGettersSetters() {
    // Données de test
    String numeroReference = "DA-2025-001";
    UUID membreDemandeurId = UUID.randomUUID();
    String nomDemandeur = "Jean Dupont";
    UUID associationId = UUID.randomUUID();
    TypeAide typeAide = TypeAide.AIDE_FINANCIERE_URGENTE;
    String titre = "Aide pour frais médicaux";
    String description = "Demande d'aide pour couvrir les frais d'hospitalisation";
    BigDecimal montantDemande = new BigDecimal("500000.00");
    StatutAide statut = StatutAide.EN_ATTENTE;
    PrioriteAide priorite = PrioriteAide.ELEVEE;

    // Test des setters
    demandeAide.setNumeroReference(numeroReference);
    demandeAide.setMembreDemandeurId(membreDemandeurId);
    demandeAide.setNomDemandeur(nomDemandeur);
    demandeAide.setAssociationId(associationId);
    demandeAide.setTypeAide(typeAide);
    demandeAide.setTitre(titre);
    demandeAide.setDescription(description);
    demandeAide.setMontantDemande(montantDemande);
    demandeAide.setStatut(statut);
    demandeAide.setPriorite(priorite);

    // Test des getters
    assertThat(demandeAide.getNumeroReference()).isEqualTo(numeroReference);
    assertThat(demandeAide.getMembreDemandeurId()).isEqualTo(membreDemandeurId);
    assertThat(demandeAide.getNomDemandeur()).isEqualTo(nomDemandeur);
    assertThat(demandeAide.getAssociationId()).isEqualTo(associationId);
    assertThat(demandeAide.getTypeAide()).isEqualTo(typeAide);
    assertThat(demandeAide.getTitre()).isEqualTo(titre);
    assertThat(demandeAide.getDescription()).isEqualTo(description);
    assertThat(demandeAide.getMontantDemande()).isEqualTo(montantDemande);
    assertThat(demandeAide.getStatut()).isEqualTo(statut);
    assertThat(demandeAide.getPriorite()).isEqualTo(priorite);
  }

  @Test
  @DisplayName("Test méthode marquerCommeModifie")
  void testMarquerCommeModifie() {
    String utilisateur = "admin@unionflow.dev";
    LocalDateTime avant = LocalDateTime.now();

    demandeAide.marquerCommeModifie(utilisateur);

    LocalDateTime apres = LocalDateTime.now();

    assertThat(demandeAide.getDateModification()).isBetween(avant, apres);
  }

  @Test
  @DisplayName("Test constructeur et setters")
  void testConstructeurEtSetters() {
    DemandeAideDTO demande = new DemandeAideDTO();
    demande.setNumeroReference("DA-2025-002");
    demande.setTitre("Test Constructeur");
    demande.setTypeAide(TypeAide.DON_MATERIEL);
    demande.setStatut(StatutAide.BROUILLON);
    demande.setPriorite(PrioriteAide.NORMALE);

    assertThat(demande.getNumeroReference()).isEqualTo("DA-2025-002");
    assertThat(demande.getTitre()).isEqualTo("Test Constructeur");
    assertThat(demande.getTypeAide()).isEqualTo(TypeAide.DON_MATERIEL);
    assertThat(demande.getStatut()).isEqualTo(StatutAide.BROUILLON);
    assertThat(demande.getPriorite()).isEqualTo(PrioriteAide.NORMALE);
  }

  @Test
  @DisplayName("Test champs spécifiques à DemandeAideDTO")
  void testChampsSpecifiques() {
    // Données de test
    String raisonRejet = "Dossier incomplet";
    LocalDateTime dateRejet = LocalDateTime.now().minusDays(2);
    UUID rejeteParId = UUID.randomUUID();
    String rejetePar = "Admin System";

    // Test des setters
    demandeAide.setRaisonRejet(raisonRejet);
    demandeAide.setDateRejet(dateRejet);
    demandeAide.setRejeteParId(rejeteParId);
    demandeAide.setRejetePar(rejetePar);

    // Test des getters
    assertThat(demandeAide.getRaisonRejet()).isEqualTo(raisonRejet);
    assertThat(demandeAide.getDateRejet()).isEqualTo(dateRejet);
    assertThat(demandeAide.getRejeteParId()).isEqualTo(rejeteParId);
    assertThat(demandeAide.getRejetePar()).isEqualTo(rejetePar);
  }

  @Test
  @DisplayName("Test validation des énumérations")
  void testValidationEnumerations() {
    // Test que toutes les énumérations sont bien supportées
    for (TypeAide type : TypeAide.values()) {
      demandeAide.setTypeAide(type);
      assertThat(demandeAide.getTypeAide()).isEqualTo(type);
    }

    for (StatutAide statut : StatutAide.values()) {
      demandeAide.setStatut(statut);
      assertThat(demandeAide.getStatut()).isEqualTo(statut);
    }

    for (PrioriteAide priorite : PrioriteAide.values()) {
      demandeAide.setPriorite(priorite);
      assertThat(demandeAide.getPriorite()).isEqualTo(priorite);
    }
  }

  @Nested
  @DisplayName("Tests getters/setters complets")
  class GettersSettersTests {

    @Test
    @DisplayName("Test tous les champs de base")
    void testTousLesChampsBase() {
      // Champs de base
      String justification = "Justification de test";
      BigDecimal montantApprouve = new BigDecimal("450000.00");
      BigDecimal montantVerse = new BigDecimal("450000.00");
      String devise = "XOF";
      String numeroMembreDemandeur = "UF-2025-000001";
      String evaluateurId = UUID.randomUUID().toString();
      String evaluateurNom = "Évaluateur Test";
      String approvateurId = UUID.randomUUID().toString();
      String approvateurNom = "Approbateur Test";
      String nomAssociation = "Association Test";
      String motifRejet = "Motif de rejet";

      demandeAide.setJustification(justification);
      demandeAide.setMontantApprouve(montantApprouve);
      demandeAide.setMontantVerse(montantVerse);
      demandeAide.setDevise(devise);
      demandeAide.setNumeroMembreDemandeur(numeroMembreDemandeur);
      demandeAide.setEvaluateurId(evaluateurId);
      demandeAide.setEvaluateurNom(evaluateurNom);
      demandeAide.setApprovateurId(approvateurId);
      demandeAide.setApprovateurNom(approvateurNom);
      demandeAide.setNomAssociation(nomAssociation);
      demandeAide.setMotifRejet(motifRejet);

      assertThat(demandeAide.getJustification()).isEqualTo(justification);
      assertThat(demandeAide.getMontantApprouve()).isEqualTo(montantApprouve);
      assertThat(demandeAide.getMontantVerse()).isEqualTo(montantVerse);
      assertThat(demandeAide.getDevise()).isEqualTo(devise);
      assertThat(demandeAide.getNumeroMembreDemandeur()).isEqualTo(numeroMembreDemandeur);
      assertThat(demandeAide.getEvaluateurId()).isEqualTo(evaluateurId);
      assertThat(demandeAide.getEvaluateurNom()).isEqualTo(evaluateurNom);
      assertThat(demandeAide.getApprovateurId()).isEqualTo(approvateurId);
      assertThat(demandeAide.getApprovateurNom()).isEqualTo(approvateurNom);
      assertThat(demandeAide.getNomAssociation()).isEqualTo(nomAssociation);
      assertThat(demandeAide.getMotifRejet()).isEqualTo(motifRejet);
    }

    @Test
    @DisplayName("Test tous les champs de dates")
    void testTousLesChampsDates() {
      LocalDateTime dateSoumission = LocalDateTime.now().minusDays(5);
      LocalDateTime dateLimiteTraitement = LocalDateTime.now().plusDays(10);
      LocalDateTime dateEvaluation = LocalDateTime.now().minusDays(2);
      LocalDateTime dateApprobation = LocalDateTime.now().minusDays(1);
      LocalDateTime dateVersement = LocalDateTime.now();
      LocalDateTime dateCloture = LocalDateTime.now();
      LocalDate dateLimite = LocalDate.now().plusDays(30);
      LocalDate dateDebutAide = LocalDate.now();
      LocalDate dateFinAide = LocalDate.now().plusDays(10);

      demandeAide.setDateSoumission(dateSoumission);
      demandeAide.setDateLimiteTraitement(dateLimiteTraitement);
      demandeAide.setDateEvaluation(dateEvaluation);
      demandeAide.setDateApprobation(dateApprobation);
      demandeAide.setDateVersement(dateVersement);
      demandeAide.setDateCloture(dateCloture);
      demandeAide.setDateLimite(dateLimite);
      demandeAide.setDateDebutAide(dateDebutAide);
      demandeAide.setDateFinAide(dateFinAide);

      assertThat(demandeAide.getDateSoumission()).isEqualTo(dateSoumission);
      assertThat(demandeAide.getDateLimiteTraitement()).isEqualTo(dateLimiteTraitement);
      assertThat(demandeAide.getDateEvaluation()).isEqualTo(dateEvaluation);
      assertThat(demandeAide.getDateApprobation()).isEqualTo(dateApprobation);
      assertThat(demandeAide.getDateVersement()).isEqualTo(dateVersement);
      assertThat(demandeAide.getDateCloture()).isEqualTo(dateCloture);
      assertThat(demandeAide.getDateLimite()).isEqualTo(dateLimite);
      assertThat(demandeAide.getDateDebutAide()).isEqualTo(dateDebutAide);
      assertThat(demandeAide.getDateFinAide()).isEqualTo(dateFinAide);
    }

    @Test
    @DisplayName("Test champs de métadonnées")
    void testChampsMetadonnees() {
      Boolean estConfidentielle = true;
      Boolean necessiteSuivi = true;
      Double scorePriorite = 85.5;
      Integer nombreVues = 10;
      Boolean justificatifsFournis = true;
      String documentsJoints = "doc1.pdf,doc2.pdf";

      demandeAide.setEstConfidentielle(estConfidentielle);
      demandeAide.setNecessiteSuivi(necessiteSuivi);
      demandeAide.setScorePriorite(scorePriorite);
      demandeAide.setNombreVues(nombreVues);
      demandeAide.setJustificatifsFournis(justificatifsFournis);
      demandeAide.setDocumentsJoints(documentsJoints);

      assertThat(demandeAide.getEstConfidentielle()).isEqualTo(estConfidentielle);
      assertThat(demandeAide.getNecessiteSuivi()).isEqualTo(necessiteSuivi);
      assertThat(demandeAide.getScorePriorite()).isEqualTo(scorePriorite);
      assertThat(demandeAide.getNombreVues()).isEqualTo(nombreVues);
      assertThat(demandeAide.getJustificatifsFournis()).isEqualTo(justificatifsFournis);
      assertThat(demandeAide.getDocumentsJoints()).isEqualTo(documentsJoints);
    }

    @Test
    @DisplayName("Test champs de versement")
    void testChampsVersement() {
      UUID membreAidantId = UUID.randomUUID();
      String nomAidant = "Aidant Test";
      String modeVersement = "WAVE_MONEY";
      String numeroTransaction = "TXN-123456";

      demandeAide.setMembreAidantId(membreAidantId);
      demandeAide.setNomAidant(nomAidant);
      demandeAide.setModeVersement(modeVersement);
      demandeAide.setNumeroTransaction(numeroTransaction);

      assertThat(demandeAide.getMembreAidantId()).isEqualTo(membreAidantId);
      assertThat(demandeAide.getNomAidant()).isEqualTo(nomAidant);
      assertThat(demandeAide.getModeVersement()).isEqualTo(modeVersement);
      assertThat(demandeAide.getNumeroTransaction()).isEqualTo(numeroTransaction);
    }

    @Test
    @DisplayName("Test champs de collections")
    void testChampsCollections() {
      List<PieceJustificativeDTO> pieces =
          List.of(
              PieceJustificativeDTO.builder()
                  .id("piece1")
                  .nomFichier("doc1.pdf")
                  .build());
      List<BeneficiaireAideDTO> beneficiaires =
          List.of(
              BeneficiaireAideDTO.builder()
                  .id("benef1")
                  .nomComplet("Bénéficiaire Test")
                  .build());
      List<HistoriqueStatutDTO> historique =
          List.of(
              HistoriqueStatutDTO.builder()
                  .id("hist1")
                  .nouveauStatut(StatutAide.EN_ATTENTE)
                  .build());
      List<CommentaireAideDTO> commentaires =
          List.of(
              CommentaireAideDTO.builder()
                  .id("comment1")
                  .contenu("Commentaire test")
                  .build());
      Map<String, Object> donneesPersonnalisees = Map.of("key1", "value1", "key2", 123);
      List<String> tags = List.of("urgent", "medical", "aide");

      demandeAide.setPiecesJustificatives(pieces);
      demandeAide.setBeneficiaires(beneficiaires);
      demandeAide.setHistoriqueStatuts(historique);
      demandeAide.setCommentaires(commentaires);
      demandeAide.setDonneesPersonnalisees(donneesPersonnalisees);
      demandeAide.setTags(tags);

      assertThat(demandeAide.getPiecesJustificatives()).isEqualTo(pieces);
      assertThat(demandeAide.getBeneficiaires()).isEqualTo(beneficiaires);
      assertThat(demandeAide.getHistoriqueStatuts()).isEqualTo(historique);
      assertThat(demandeAide.getCommentaires()).isEqualTo(commentaires);
      assertThat(demandeAide.getDonneesPersonnalisees()).isEqualTo(donneesPersonnalisees);
      assertThat(demandeAide.getTags()).isEqualTo(tags);
    }

    @Test
    @DisplayName("Test champs de localisation et contact")
    void testChampsLocalisationEtContact() {
      LocalisationDTO localisation =
          LocalisationDTO.builder()
              .latitude(14.7167)
              .longitude(-17.4677)
              .adresseComplete("Adresse test")
              .build();
      ContactUrgenceDTO contactUrgence =
          ContactUrgenceDTO.builder()
              .nomComplet("Contact Urgence")
              .telephonePrincipal("+221771234567")
              .build();

      demandeAide.setLocalisation(localisation);
      demandeAide.setContactUrgence(contactUrgence);

      assertThat(demandeAide.getLocalisation()).isEqualTo(localisation);
      assertThat(demandeAide.getContactUrgence()).isEqualTo(contactUrgence);
    }
  }

  @Nested
  @DisplayName("Tests méthodes utilitaires")
  class MethodesUtilitairesTests {

    @ParameterizedTest
    @EnumSource(StatutAide.class)
    @DisplayName("estModifiable - tous les statuts")
    void testEstModifiable(StatutAide statut) {
      demandeAide.setStatut(statut);
      assertThat(demandeAide.estModifiable()).isEqualTo(statut.permetModification());
    }

    @ParameterizedTest
    @EnumSource(StatutAide.class)
    @DisplayName("peutEtreAnnulee - tous les statuts")
    void testPeutEtreAnnulee(StatutAide statut) {
      demandeAide.setStatut(statut);
      assertThat(demandeAide.peutEtreAnnulee()).isEqualTo(statut.permetAnnulation());
    }

    @ParameterizedTest
    @EnumSource(PrioriteAide.class)
    @DisplayName("estUrgente - toutes les priorités")
    void testEstUrgente(PrioriteAide priorite) {
      demandeAide.setPriorite(priorite);
      assertThat(demandeAide.estUrgente()).isEqualTo(priorite.isUrgente());
    }

    @ParameterizedTest
    @EnumSource(StatutAide.class)
    @DisplayName("estTerminee - tous les statuts")
    void testEstTerminee(StatutAide statut) {
      demandeAide.setStatut(statut);
      assertThat(demandeAide.estTerminee()).isEqualTo(statut.isEstFinal());
    }

    @ParameterizedTest
    @EnumSource(StatutAide.class)
    @DisplayName("estEnSucces - tous les statuts")
    void testEstEnSucces(StatutAide statut) {
      demandeAide.setStatut(statut);
      assertThat(demandeAide.estEnSucces()).isEqualTo(statut.isSucces());
    }

    @ParameterizedTest
    @CsvSource({
        "BROUILLON, 5.0",
        "SOUMISE, 10.0",
        "EN_ATTENTE, 20.0",
        "EN_COURS_EVALUATION, 40.0",
        "INFORMATIONS_REQUISES, 35.0",
        "APPROUVEE, 60.0",
        "APPROUVEE_PARTIELLEMENT, 60.0",
        "EN_COURS_TRAITEMENT, 70.0",
        "EN_COURS_VERSEMENT, 85.0",
        "VERSEE, 100.0",
        "LIVREE, 100.0",
        "TERMINEE, 100.0",
        "REJETEE, 100.0",
        "ANNULEE, 100.0",
        "EXPIREE, 100.0",
        "SUSPENDUE, 50.0",
        "EN_SUIVI, 95.0",
        "CLOTUREE, 100.0"
    })
    @DisplayName("getPourcentageAvancement - tous les statuts")
    void testGetPourcentageAvancement(StatutAide statut, Double expected) {
      demandeAide.setStatut(statut);
      assertThat(demandeAide.getPourcentageAvancement()).isEqualTo(expected);
    }

    @Test
    @DisplayName("getPourcentageAvancement - statut null")
    void testGetPourcentageAvancementStatutNull() {
      demandeAide.setStatut(null);
      assertThat(demandeAide.getPourcentageAvancement()).isEqualTo(0.0);
    }

    @Test
    @DisplayName("getDelaiRestantHeures - délai non dépassé")
    void testGetDelaiRestantHeuresNonDepasse() {
      LocalDateTime dateLimite = LocalDateTime.now().plusHours(24);
      demandeAide.setDateLimiteTraitement(dateLimite);

      long delai = demandeAide.getDelaiRestantHeures();
      assertThat(delai).isGreaterThan(0);
      assertThat(delai).isLessThanOrEqualTo(24);
    }

    @Test
    @DisplayName("getDelaiRestantHeures - délai dépassé")
    void testGetDelaiRestantHeuresDepasse() {
      LocalDateTime dateLimite = LocalDateTime.now().minusHours(1);
      demandeAide.setDateLimiteTraitement(dateLimite);

      assertThat(demandeAide.getDelaiRestantHeures()).isEqualTo(0);
    }

    @Test
    @DisplayName("getDelaiRestantHeures - date limite null")
    void testGetDelaiRestantHeuresNull() {
      demandeAide.setDateLimiteTraitement(null);
      assertThat(demandeAide.getDelaiRestantHeures()).isEqualTo(-1);
    }

    @Test
    @DisplayName("estDelaiDepasse - délai dépassé")
    void testEstDelaiDepasse() {
      LocalDateTime dateLimite = LocalDateTime.now().minusHours(1);
      demandeAide.setDateLimiteTraitement(dateLimite);

      assertThat(demandeAide.estDelaiDepasse()).isTrue();
    }

    @Test
    @DisplayName("estDelaiDepasse - délai non dépassé")
    void testEstDelaiDepasseNonDepasse() {
      LocalDateTime dateLimite = LocalDateTime.now().plusHours(24);
      demandeAide.setDateLimiteTraitement(dateLimite);

      assertThat(demandeAide.estDelaiDepasse()).isFalse();
    }

    @Test
    @DisplayName("getDureeTraitementJours - avec date clôture")
    void testGetDureeTraitementJoursAvecCloture() {
      LocalDateTime dateCreation = LocalDateTime.now().minusDays(10);
      LocalDateTime dateCloture = LocalDateTime.now().minusDays(2);
      demandeAide.setDateCreation(dateCreation);
      demandeAide.setDateCloture(dateCloture);

      assertThat(demandeAide.getDureeTraitementJours()).isEqualTo(8);
    }

    @Test
    @DisplayName("getDureeTraitementJours - sans date clôture")
    void testGetDureeTraitementJoursSansCloture() {
      LocalDateTime dateCreation = LocalDateTime.now().minusDays(5);
      demandeAide.setDateCreation(dateCreation);
      demandeAide.setDateCloture(null);

      long duree = demandeAide.getDureeTraitementJours();
      assertThat(duree).isGreaterThanOrEqualTo(5);
    }

    @Test
    @DisplayName("getDureeTraitementJours - date création null")
    void testGetDureeTraitementJoursDateCreationNull() {
      demandeAide.setDateCreation(null);
      assertThat(demandeAide.getDureeTraitementJours()).isEqualTo(0);
    }

    @Test
    @DisplayName("getStatutLibelle - statut défini")
    void testGetStatutLibelle() {
      demandeAide.setStatut(StatutAide.APPROUVEE);
      assertThat(demandeAide.getStatutLibelle()).isEqualTo(StatutAide.APPROUVEE.getLibelle());
    }

    @Test
    @DisplayName("getStatutLibelle - statut null")
    void testGetStatutLibelleNull() {
      demandeAide.setStatut(null);
      assertThat(demandeAide.getStatutLibelle()).isEqualTo("Non défini");
    }

    @Test
    @DisplayName("getPrioriteLibelle - priorité définie")
    void testGetPrioriteLibelle() {
      demandeAide.setPriorite(PrioriteAide.ELEVEE);
      assertThat(demandeAide.getPrioriteLibelle()).isEqualTo(PrioriteAide.ELEVEE.getLibelle());
    }

    @Test
    @DisplayName("getPrioriteLibelle - priorité null")
    void testGetPrioriteLibelleNull() {
      demandeAide.setPriorite(null);
      assertThat(demandeAide.getPrioriteLibelle()).isEqualTo("Normale");
    }

    @Test
    @DisplayName("approuver - met à jour tous les champs")
    void testApprouver() {
      UUID evaluateurId = UUID.randomUUID();
      String nomEvaluateur = "Évaluateur Test";
      BigDecimal montantApprouve = new BigDecimal("450000.00");
      String commentaires = "Demande approuvée";

      demandeAide.approuver(evaluateurId, nomEvaluateur, montantApprouve, commentaires);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.APPROUVEE);
      assertThat(demandeAide.getEvaluateurId()).isEqualTo(evaluateurId.toString());
      assertThat(demandeAide.getEvaluateurNom()).isEqualTo(nomEvaluateur);
      assertThat(demandeAide.getMontantApprouve()).isEqualTo(montantApprouve);
      assertThat(demandeAide.getCommentairesEvaluateur()).isEqualTo(commentaires);
      assertThat(demandeAide.getDateEvaluation()).isNotNull();
      assertThat(demandeAide.getDateApprobation()).isNotNull();
    }

    @Test
    @DisplayName("rejeter - met à jour tous les champs")
    void testRejeter() {
      UUID evaluateurId = UUID.randomUUID();
      String nomEvaluateur = "Évaluateur Test";
      String raison = "Dossier incomplet";

      demandeAide.rejeter(evaluateurId, nomEvaluateur, raison);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.REJETEE);
      assertThat(demandeAide.getRejeteParId()).isEqualTo(evaluateurId);
      assertThat(demandeAide.getRejetePar()).isEqualTo(nomEvaluateur);
      assertThat(demandeAide.getRaisonRejet()).isEqualTo(raison);
      assertThat(demandeAide.getDateRejet()).isNotNull();
      assertThat(demandeAide.getDateEvaluation()).isNotNull();
    }

    @Test
    @DisplayName("demarrerAide - met à jour tous les champs")
    void testDemarrerAide() {
      UUID aidantId = UUID.randomUUID();
      String nomAidant = "Aidant Test";

      demandeAide.demarrerAide(aidantId, nomAidant);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.EN_COURS_TRAITEMENT);
      assertThat(demandeAide.getMembreAidantId()).isEqualTo(aidantId);
      assertThat(demandeAide.getNomAidant()).isEqualTo(nomAidant);
      assertThat(demandeAide.getDateDebutAide()).isEqualTo(LocalDate.now());
    }

    @Test
    @DisplayName("terminerAvecVersement - met à jour tous les champs")
    void testTerminerAvecVersement() {
      BigDecimal montantVerse = new BigDecimal("450000.00");
      String modeVersement = "WAVE_MONEY";
      String numeroTransaction = "TXN-123456";

      demandeAide.terminerAvecVersement(montantVerse, modeVersement, numeroTransaction);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.TERMINEE);
      assertThat(demandeAide.getMontantVerse()).isEqualTo(montantVerse);
      assertThat(demandeAide.getModeVersement()).isEqualTo(modeVersement);
      assertThat(demandeAide.getNumeroTransaction()).isEqualTo(numeroTransaction);
      assertThat(demandeAide.getDateVersement()).isNotNull();
      assertThat(demandeAide.getDateFinAide()).isEqualTo(LocalDate.now());
    }

    @Test
    @DisplayName("incrementerVues - null devient 1")
    void testIncrementerVuesNull() {
      demandeAide.setNombreVues(null);
      demandeAide.incrementerVues();
      assertThat(demandeAide.getNombreVues()).isEqualTo(1);
    }

    @Test
    @DisplayName("incrementerVues - incrémente la valeur existante")
    void testIncrementerVuesExistant() {
      demandeAide.setNombreVues(5);
      demandeAide.incrementerVues();
      assertThat(demandeAide.getNombreVues()).isEqualTo(6);
    }

    @Test
    @DisplayName("genererNumeroReference - génère un numéro valide")
    void testGenererNumeroReference() {
      String numero = DemandeAideDTO.genererNumeroReference();
      assertThat(numero).matches("^DA-\\d{4}-\\d{6}$");
      assertThat(numero).startsWith("DA-");
    }

    @Test
    @DisplayName("estModifiable - statut null")
    void testEstModifiableStatutNull() {
      demandeAide.setStatut(null);
      assertThat(demandeAide.estModifiable()).isFalse();
    }

    @Test
    @DisplayName("peutEtreAnnulee - statut null")
    void testPeutEtreAnnuleeStatutNull() {
      demandeAide.setStatut(null);
      assertThat(demandeAide.peutEtreAnnulee()).isFalse();
    }

    @Test
    @DisplayName("estUrgente - priorité null")
    void testEstUrgentePrioriteNull() {
      demandeAide.setPriorite(null);
      assertThat(demandeAide.estUrgente()).isFalse();
    }

    @Test
    @DisplayName("estTerminee - statut null")
    void testEstTermineeStatutNull() {
      demandeAide.setStatut(null);
      assertThat(demandeAide.estTerminee()).isFalse();
    }

    @Test
    @DisplayName("estEnSucces - statut null")
    void testEstEnSuccesStatutNull() {
      demandeAide.setStatut(null);
      assertThat(demandeAide.estEnSucces()).isFalse();
    }

    @Test
    @DisplayName("estDelaiDepasse - date limite null")
    void testEstDelaiDepasseDateNull() {
      demandeAide.setDateLimiteTraitement(null);
      // getDelaiRestantHeures() retourne -1 si null, donc estDelaiDepasse() retourne false
      assertThat(demandeAide.estDelaiDepasse()).isFalse();
    }

    @Test
    @DisplayName("getDureeTraitementJours - date clôture null utilise maintenant")
    void testGetDureeTraitementJoursDateClotureNull() {
      LocalDateTime dateCreation = LocalDateTime.now().minusDays(5);
      demandeAide.setDateCreation(dateCreation);
      demandeAide.setDateCloture(null);

      long duree = demandeAide.getDureeTraitementJours();
      // Devrait utiliser LocalDateTime.now() comme dateFin
      assertThat(duree).isGreaterThanOrEqualTo(5);
    }

    @Test
    @DisplayName("marquerCommeModifie - override vérifie dateModification mise à jour")
    void testMarquerCommeModifieOverride() {
      String utilisateur = "admin@unionflow.dev";
      LocalDateTime dateAvant = LocalDateTime.now().minusSeconds(1);
      demandeAide.setDateModification(dateAvant);
      demandeAide.setVersion(5L);

      demandeAide.marquerCommeModifie(utilisateur);

      // L'override met à jour dateModification avant d'appeler super
      assertThat(demandeAide.getDateModification()).isAfter(dateAvant);
      assertThat(demandeAide.getModifiePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getVersion()).isEqualTo(6L);
    }

    @Test
    @DisplayName("approuver - vérifie appel à marquerCommeModifie")
    void testApprouverAppelleMarquerCommeModifie() {
      UUID evaluateurId = UUID.randomUUID();
      String nomEvaluateur = "Évaluateur Test";
      BigDecimal montantApprouve = new BigDecimal("450000.00");
      String commentaires = "Demande approuvée";
      LocalDateTime dateAvant = LocalDateTime.now().minusSeconds(1);
      demandeAide.setDateModification(dateAvant);

      demandeAide.approuver(evaluateurId, nomEvaluateur, montantApprouve, commentaires);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.APPROUVEE);
      assertThat(demandeAide.getModifiePar()).isEqualTo(nomEvaluateur);
      assertThat(demandeAide.getDateModification()).isAfter(dateAvant);
    }

    @Test
    @DisplayName("rejeter - vérifie appel à marquerCommeModifie")
    void testRejeterAppelleMarquerCommeModifie() {
      UUID evaluateurId = UUID.randomUUID();
      String nomEvaluateur = "Évaluateur Test";
      String raison = "Dossier incomplet";
      LocalDateTime dateAvant = LocalDateTime.now().minusSeconds(1);
      demandeAide.setDateModification(dateAvant);

      demandeAide.rejeter(evaluateurId, nomEvaluateur, raison);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.REJETEE);
      assertThat(demandeAide.getModifiePar()).isEqualTo(nomEvaluateur);
      assertThat(demandeAide.getDateModification()).isAfter(dateAvant);
    }

    @Test
    @DisplayName("demarrerAide - vérifie appel à marquerCommeModifie")
    void testDemarrerAideAppelleMarquerCommeModifie() {
      UUID aidantId = UUID.randomUUID();
      String nomAidant = "Aidant Test";
      LocalDateTime dateAvant = LocalDateTime.now().minusSeconds(1);
      demandeAide.setDateModification(dateAvant);

      demandeAide.demarrerAide(aidantId, nomAidant);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.EN_COURS_TRAITEMENT);
      assertThat(demandeAide.getModifiePar()).isEqualTo(nomAidant);
      assertThat(demandeAide.getDateModification()).isAfter(dateAvant);
    }

    @Test
    @DisplayName("terminerAvecVersement - vérifie appel à marquerCommeModifie")
    void testTerminerAvecVersementAppelleMarquerCommeModifie() {
      BigDecimal montantVerse = new BigDecimal("450000.00");
      String modeVersement = "WAVE_MONEY";
      String numeroTransaction = "TXN-123456";
      LocalDateTime dateAvant = LocalDateTime.now().minusSeconds(1);
      demandeAide.setDateModification(dateAvant);

      demandeAide.terminerAvecVersement(montantVerse, modeVersement, numeroTransaction);

      assertThat(demandeAide.getStatut()).isEqualTo(StatutAide.TERMINEE);
      assertThat(demandeAide.getModifiePar()).isEqualTo("SYSTEM");
      assertThat(demandeAide.getDateModification()).isAfter(dateAvant);
    }

    @Test
    @DisplayName("getMontantDemande - getter explicite")
    void testGetMontantDemande() {
      BigDecimal montant = new BigDecimal("500000.00");
      demandeAide.setMontantDemande(montant);
      assertThat(demandeAide.getMontantDemande()).isEqualTo(montant);
    }

    @Test
    @DisplayName("getTypeAide - getter explicite")
    void testGetTypeAide() {
      TypeAide typeAide = TypeAide.AIDE_FINANCIERE_URGENTE;
      demandeAide.setTypeAide(typeAide);
      assertThat(demandeAide.getTypeAide()).isEqualTo(typeAide);
    }
  }

  @Nested
  @DisplayName("Tests constructeur et initialisation")
  class ConstructeurTests {

    @Test
    @DisplayName("Constructeur par défaut - initialise les valeurs")
    void testConstructeurParDefaut() {
      DemandeAideDTO demande = new DemandeAideDTO();

      assertThat(demande.getStatut()).isEqualTo(StatutAide.EN_ATTENTE);
      assertThat(demande.getPriorite()).isEqualTo(PrioriteAide.NORMALE);
      assertThat(demande.getDevise()).isEqualTo("XOF");
      assertThat(demande.getNombreVues()).isEqualTo(0);
      assertThat(demande.getNumeroReference()).isNotNull();
      assertThat(demande.getNumeroReference()).matches("^DA-\\d{4}-\\d{6}$");
    }
  }

  @Nested
  @DisplayName("Tests getters/setters manquants")
  class GettersSettersManquantsTests {

    @Test
    @DisplayName("Test tous les champs additionnels")
    void testTousLesChampsAdditionnels() {
      UUID membreAidantId = UUID.randomUUID();
      String nomAidant = "Aidant Test";
      String modeVersement = "WAVE_MONEY";
      String numeroTransaction = "TXN-123456";
      UUID rejeteParId = UUID.randomUUID();
      String rejetePar = "Rejeteur Test";
      LocalDateTime dateRejet = LocalDateTime.now().minusDays(1);
      String raisonRejet = "Raison de rejet";
      String motifRejet = "Motif de rejet";
      String commentairesEvaluateur = "Commentaires évaluateur";
      String evaluateurId = UUID.randomUUID().toString();
      String evaluateurNom = "Évaluateur Test";
      String approvateurId = UUID.randomUUID().toString();
      String approvateurNom = "Approbateur Test";
      String numeroMembreDemandeur = "UF-2025-000001";
      String nomAssociation = "Association Test";

      demandeAide.setMembreAidantId(membreAidantId);
      demandeAide.setNomAidant(nomAidant);
      demandeAide.setModeVersement(modeVersement);
      demandeAide.setNumeroTransaction(numeroTransaction);
      demandeAide.setRejeteParId(rejeteParId);
      demandeAide.setRejetePar(rejetePar);
      demandeAide.setDateRejet(dateRejet);
      demandeAide.setRaisonRejet(raisonRejet);
      demandeAide.setMotifRejet(motifRejet);
      demandeAide.setCommentairesEvaluateur(commentairesEvaluateur);
      demandeAide.setEvaluateurId(evaluateurId);
      demandeAide.setEvaluateurNom(evaluateurNom);
      demandeAide.setApprovateurId(approvateurId);
      demandeAide.setApprovateurNom(approvateurNom);
      demandeAide.setNumeroMembreDemandeur(numeroMembreDemandeur);
      demandeAide.setNomAssociation(nomAssociation);

      assertThat(demandeAide.getMembreAidantId()).isEqualTo(membreAidantId);
      assertThat(demandeAide.getNomAidant()).isEqualTo(nomAidant);
      assertThat(demandeAide.getModeVersement()).isEqualTo(modeVersement);
      assertThat(demandeAide.getNumeroTransaction()).isEqualTo(numeroTransaction);
      assertThat(demandeAide.getRejeteParId()).isEqualTo(rejeteParId);
      assertThat(demandeAide.getRejetePar()).isEqualTo(rejetePar);
      assertThat(demandeAide.getDateRejet()).isEqualTo(dateRejet);
      assertThat(demandeAide.getRaisonRejet()).isEqualTo(raisonRejet);
      assertThat(demandeAide.getMotifRejet()).isEqualTo(motifRejet);
      assertThat(demandeAide.getCommentairesEvaluateur()).isEqualTo(commentairesEvaluateur);
      assertThat(demandeAide.getEvaluateurId()).isEqualTo(evaluateurId);
      assertThat(demandeAide.getEvaluateurNom()).isEqualTo(evaluateurNom);
      assertThat(demandeAide.getApprovateurId()).isEqualTo(approvateurId);
      assertThat(demandeAide.getApprovateurNom()).isEqualTo(approvateurNom);
      assertThat(demandeAide.getNumeroMembreDemandeur()).isEqualTo(numeroMembreDemandeur);
      assertThat(demandeAide.getNomAssociation()).isEqualTo(nomAssociation);
    }
  }

  @Nested
  @DisplayName("Tests méthodes BaseDTO héritées")
  class MethodesBaseDTOTests {

    @Test
    @DisplayName("marquerCommeNouveau - initialise tous les champs")
    void testMarquerCommeNouveau() {
      String utilisateur = "admin@unionflow.dev";
      LocalDateTime avant = LocalDateTime.now();

      demandeAide.marquerCommeNouveau(utilisateur);

      LocalDateTime apres = LocalDateTime.now();

      assertThat(demandeAide.getDateCreation()).isBetween(avant, apres);
      assertThat(demandeAide.getDateModification()).isBetween(avant, apres);
      assertThat(demandeAide.getCreePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getModifiePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getVersion()).isEqualTo(0L);
      assertThat(demandeAide.getActif()).isTrue();
    }

    @Test
    @DisplayName("marquerCommeModifie - version null")
    void testMarquerCommeModifieVersionNull() {
      String utilisateur = "admin@unionflow.dev";
      demandeAide.setVersion(null);

      demandeAide.marquerCommeModifie(utilisateur);

      assertThat(demandeAide.getModifiePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getDateModification()).isNotNull();
      // Si version est null, elle reste null (pas d'incrémentation)
      assertThat(demandeAide.getVersion()).isNull();
    }

    @Test
    @DisplayName("marquerCommeModifie - version non null")
    void testMarquerCommeModifieVersionNonNull() {
      String utilisateur = "admin@unionflow.dev";
      demandeAide.setVersion(5L);

      demandeAide.marquerCommeModifie(utilisateur);

      assertThat(demandeAide.getModifiePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getDateModification()).isNotNull();
      assertThat(demandeAide.getVersion()).isEqualTo(6L);
    }

    @Test
    @DisplayName("desactiver - désactive l'entité")
    void testDesactiver() {
      String utilisateur = "admin@unionflow.dev";
      demandeAide.setActif(true);

      demandeAide.desactiver(utilisateur);

      assertThat(demandeAide.getActif()).isFalse();
      assertThat(demandeAide.getModifiePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getDateModification()).isNotNull();
    }

    @Test
    @DisplayName("reactiver - réactive l'entité")
    void testReactiver() {
      String utilisateur = "admin@unionflow.dev";
      demandeAide.setActif(false);

      demandeAide.reactiver(utilisateur);

      assertThat(demandeAide.getActif()).isTrue();
      assertThat(demandeAide.getModifiePar()).isEqualTo(utilisateur);
      assertThat(demandeAide.getDateModification()).isNotNull();
    }

    @Test
    @DisplayName("isNouveau - id null")
    void testIsNouveauIdNull() {
      demandeAide.setId(null);
      assertThat(demandeAide.isNouveau()).isTrue();
    }

    @Test
    @DisplayName("isNouveau - id non null")
    void testIsNouveauIdNonNull() {
      demandeAide.setId(UUID.randomUUID());
      assertThat(demandeAide.isNouveau()).isFalse();
    }

    @Test
    @DisplayName("isActif - actif true")
    void testIsActifTrue() {
      demandeAide.setActif(true);
      assertThat(demandeAide.isActif()).isTrue();
    }

    @Test
    @DisplayName("isActif - actif false")
    void testIsActifFalse() {
      demandeAide.setActif(false);
      assertThat(demandeAide.isActif()).isFalse();
    }

    @Test
    @DisplayName("isActif - actif null")
    void testIsActifNull() {
      demandeAide.setActif(null);
      assertThat(demandeAide.isActif()).isFalse();
    }

    @Test
    @DisplayName("equals - objets égaux")
    void testEqualsObjetsEgaux() {
      UUID id = UUID.randomUUID();
      LocalDateTime dateCreation = LocalDateTime.of(2025, 1, 15, 10, 0);
      LocalDateTime dateModification = LocalDateTime.of(2025, 1, 16, 11, 0);
      String creePar = "createur@test.com";
      String modifiePar = "modificateur@test.com";
      Long version = 5L;
      Boolean actif = true;
      String numeroReference = "DA-2025-000001";
      TypeAide typeAide = TypeAide.AIDE_FINANCIERE_URGENTE;
      String titre = "Titre test";
      String description = "Description test";
      StatutAide statut = StatutAide.EN_ATTENTE;
      PrioriteAide priorite = PrioriteAide.NORMALE;
      
      demandeAide.setId(id);
      demandeAide.setDateCreation(dateCreation);
      demandeAide.setDateModification(dateModification);
      demandeAide.setCreePar(creePar);
      demandeAide.setModifiePar(modifiePar);
      demandeAide.setVersion(version);
      demandeAide.setActif(actif);
      demandeAide.setNumeroReference(numeroReference);
      demandeAide.setTypeAide(typeAide);
      demandeAide.setTitre(titre);
      demandeAide.setDescription(description);
      demandeAide.setStatut(statut);
      demandeAide.setPriorite(priorite);

      DemandeAideDTO autre = new DemandeAideDTO();
      autre.setId(id);
      autre.setDateCreation(dateCreation);
      autre.setDateModification(dateModification);
      autre.setCreePar(creePar);
      autre.setModifiePar(modifiePar);
      autre.setVersion(version);
      autre.setActif(actif);
      autre.setNumeroReference(numeroReference);
      autre.setTypeAide(typeAide);
      autre.setTitre(titre);
      autre.setDescription(description);
      autre.setStatut(statut);
      autre.setPriorite(priorite);

      assertThat(demandeAide).isEqualTo(autre);
      assertThat(demandeAide.hashCode()).isEqualTo(autre.hashCode());
    }

    @Test
    @DisplayName("equals - objets différents")
    void testEqualsObjetsDifferent() {
      demandeAide.setId(UUID.randomUUID());

      DemandeAideDTO autre = new DemandeAideDTO();
      autre.setId(UUID.randomUUID());

      assertThat(demandeAide).isNotEqualTo(autre);
    }

    @Test
    @DisplayName("equals - id null")
    void testEqualsIdNull() {
      demandeAide.setId(null);

      DemandeAideDTO autre = new DemandeAideDTO();
      autre.setId(null);

      // Si id est null, equals retourne false
      assertThat(demandeAide).isNotEqualTo(autre);
    }

    @Test
    @DisplayName("hashCode - id null (via BaseDTO)")
    void testHashCodeIdNull() throws Exception {
      // Créer un BaseDTO directement pour tester hashCode avec id null
      // car DemandeAideDTO utilise @EqualsAndHashCode(callSuper = true) qui inclut tous les champs
      dev.lions.unionflow.server.api.dto.base.BaseDTO baseDTO = 
          new dev.lions.unionflow.server.api.dto.base.BaseDTO() {};
      
      // Utiliser la réflexion pour mettre id à null
      java.lang.reflect.Field idField = dev.lions.unionflow.server.api.dto.base.BaseDTO.class.getDeclaredField("id");
      idField.setAccessible(true);
      idField.set(baseDTO, null);
      
      // hashCode() retourne 0 si id est null selon BaseDTO.hashCode()
      int hashCode = baseDTO.hashCode();
      assertThat(hashCode).isEqualTo(0);
    }

    @Test
    @DisplayName("toString - contient les informations")
    void testToString() {
      UUID id = UUID.randomUUID();
      demandeAide.setId(id);
      demandeAide.setTitre("Test");

      String toString = demandeAide.toString();
      assertThat(toString).contains("DemandeAideDTO");
      assertThat(toString).contains(id.toString());
    }

    @Test
    @DisplayName("Test tous les champs BaseDTO")
    void testTousLesChampsBaseDTO() {
      UUID id = UUID.randomUUID();
      LocalDateTime dateCreation = LocalDateTime.of(2025, 1, 15, 10, 0);
      LocalDateTime dateModification = LocalDateTime.of(2025, 1, 16, 11, 0);
      String creePar = "createur@test.com";
      String modifiePar = "modificateur@test.com";
      Long version = 5L;
      Boolean actif = true;

      demandeAide.setId(id);
      demandeAide.setDateCreation(dateCreation);
      demandeAide.setDateModification(dateModification);
      demandeAide.setCreePar(creePar);
      demandeAide.setModifiePar(modifiePar);
      demandeAide.setVersion(version);
      demandeAide.setActif(actif);

      assertThat(demandeAide.getId()).isEqualTo(id);
      assertThat(demandeAide.getDateCreation()).isEqualTo(dateCreation);
      assertThat(demandeAide.getDateModification()).isEqualTo(dateModification);
      assertThat(demandeAide.getCreePar()).isEqualTo(creePar);
      assertThat(demandeAide.getModifiePar()).isEqualTo(modifiePar);
      assertThat(demandeAide.getVersion()).isEqualTo(version);
      assertThat(demandeAide.getActif()).isEqualTo(actif);
    }
  }
}
