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

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

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
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;

/**
 * Tests unitaires complets pour AideDTO.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-10
 */
@DisplayName("Tests AideDTO")
class AideDTOBasicTest {

  private AideDTO aide;

  @BeforeEach
  void setUp() {
    aide = new AideDTO();
  }

  @Nested
  @DisplayName("Tests de Construction")
  class ConstructionTests {

    @Test
    @DisplayName("Constructeur par défaut - Initialisation correcte")
    void testConstructeurParDefaut() {
      AideDTO newAide = new AideDTO();

      assertThat(newAide.getId()).isNotNull();
      assertThat(newAide.getDateCreation()).isNotNull();
      assertThat(newAide.isActif()).isTrue();
      assertThat(newAide.getVersion()).isEqualTo(0L);
      assertThat(newAide.getStatut()).isEqualTo("EN_ATTENTE");
      assertThat(newAide.getDevise()).isEqualTo("XOF");
      assertThat(newAide.getPriorite()).isEqualTo("NORMALE");
      assertThat(newAide.getNumeroReference()).isNotNull();
      assertThat(newAide.getNumeroReference()).matches("^AIDE-\\d{4}-[A-Z0-9]{6}$");
    }

    @Test
    @DisplayName("Constructeur avec paramètres - Initialisation correcte")
    void testConstructeurAvecParametres() {
      UUID membreDemandeurId = UUID.randomUUID();
      UUID associationId = UUID.randomUUID();
      String typeAide = "FINANCIERE";
      String titre = "Aide pour frais médicaux";

      AideDTO newAide = new AideDTO(membreDemandeurId, associationId, typeAide, titre);

      assertThat(newAide.getMembreDemandeurId()).isEqualTo(membreDemandeurId);
      assertThat(newAide.getAssociationId()).isEqualTo(associationId);
      assertThat(newAide.getTypeAide()).isEqualTo(typeAide);
      assertThat(newAide.getTitre()).isEqualTo(titre);
      assertThat(newAide.getStatut()).isEqualTo("EN_ATTENTE");
    }
  }

  @Nested
  @DisplayName("Tests Getters/Setters")
  class GettersSettersTests {

    @Test
    @DisplayName("Test tous les getters/setters - Partie 1")
    void testTousLesGettersSettersPart1() {
      // Données de test
      String numeroReference = "AIDE-2025-ABC123";
      UUID membreDemandeurId = UUID.randomUUID();
      String nomDemandeur = "Jean Dupont";
      String numeroMembreDemandeur = "UF-2025-12345678";
      UUID associationId = UUID.randomUUID();
      String nomAssociation = "Lions Club Dakar";
      String typeAide = "FINANCIERE";
      String titre = "Aide pour frais médicaux";
      String description = "Demande d'aide pour couvrir les frais d'hospitalisation";
      BigDecimal montantDemande = new BigDecimal("500000.00");
      String devise = "XOF";
      String statut = "EN_COURS_EVALUATION";
      String priorite = "HAUTE";

      // Test des setters
      aide.setNumeroReference(numeroReference);
      aide.setMembreDemandeurId(membreDemandeurId);
      aide.setNomDemandeur(nomDemandeur);
      aide.setNumeroMembreDemandeur(numeroMembreDemandeur);
      aide.setAssociationId(associationId);
      aide.setNomAssociation(nomAssociation);
      aide.setTypeAide(typeAide);
      aide.setTitre(titre);
      aide.setDescription(description);
      aide.setMontantDemande(montantDemande);
      aide.setDevise(devise);
      aide.setStatut(statut);
      aide.setPriorite(priorite);

      // Test des getters
      assertThat(aide.getNumeroReference()).isEqualTo(numeroReference);
      assertThat(aide.getMembreDemandeurId()).isEqualTo(membreDemandeurId);
      assertThat(aide.getNomDemandeur()).isEqualTo(nomDemandeur);
      assertThat(aide.getNumeroMembreDemandeur()).isEqualTo(numeroMembreDemandeur);
      assertThat(aide.getAssociationId()).isEqualTo(associationId);
      assertThat(aide.getNomAssociation()).isEqualTo(nomAssociation);
      assertThat(aide.getTypeAide()).isEqualTo(typeAide);
      assertThat(aide.getTitre()).isEqualTo(titre);
      assertThat(aide.getDescription()).isEqualTo(description);
      assertThat(aide.getMontantDemande()).isEqualTo(montantDemande);
      assertThat(aide.getDevise()).isEqualTo(devise);
      assertThat(aide.getStatut()).isEqualTo(statut);
      assertThat(aide.getPriorite()).isEqualTo(priorite);
    }

    @Test
    @DisplayName("Test tous les getters/setters - Partie 2")
    void testTousLesGettersSettersPart2() {
      // Données de test
      LocalDate dateLimite = LocalDate.now().plusMonths(3);
      Boolean justificatifsFournis = true;
      String documentsJoints = "certificat_medical.pdf,facture_hopital.pdf";
      UUID membreEvaluateurId = UUID.randomUUID();
      String nomEvaluateur = "Marie Martin";
      LocalDateTime dateEvaluation = LocalDateTime.now();
      String commentairesEvaluateur = "Dossier complet, situation vérifiée";
      BigDecimal montantApprouve = new BigDecimal("400000.00");
      LocalDateTime dateApprobation = LocalDateTime.now();
      UUID membreAidantId = UUID.randomUUID();
      String nomAidant = "Paul Durand";
      LocalDate dateDebutAide = LocalDate.now();
      LocalDate dateFinAide = LocalDate.now().plusMonths(6);
      BigDecimal montantVerse = new BigDecimal("400000.00");
      String modeVersement = "WAVE_MONEY";
      String numeroTransaction = "TXN123456789";
      LocalDateTime dateVersement = LocalDateTime.now();

      // Test des setters
      aide.setDateLimite(dateLimite);
      aide.setJustificatifsFournis(justificatifsFournis);
      aide.setDocumentsJoints(documentsJoints);
      aide.setMembreEvaluateurId(membreEvaluateurId);
      aide.setNomEvaluateur(nomEvaluateur);
      aide.setDateEvaluation(dateEvaluation);
      aide.setCommentairesEvaluateur(commentairesEvaluateur);
      aide.setMontantApprouve(montantApprouve);
      aide.setDateApprobation(dateApprobation);
      aide.setMembreAidantId(membreAidantId);
      aide.setNomAidant(nomAidant);
      aide.setDateDebutAide(dateDebutAide);
      aide.setDateFinAide(dateFinAide);
      aide.setMontantVerse(montantVerse);
      aide.setModeVersement(modeVersement);
      aide.setNumeroTransaction(numeroTransaction);
      aide.setDateVersement(dateVersement);

      // Test des getters
      assertThat(aide.getDateLimite()).isEqualTo(dateLimite);
      assertThat(aide.getJustificatifsFournis()).isEqualTo(justificatifsFournis);
      assertThat(aide.getDocumentsJoints()).isEqualTo(documentsJoints);
      assertThat(aide.getMembreEvaluateurId()).isEqualTo(membreEvaluateurId);
      assertThat(aide.getNomEvaluateur()).isEqualTo(nomEvaluateur);
      assertThat(aide.getDateEvaluation()).isEqualTo(dateEvaluation);
      assertThat(aide.getCommentairesEvaluateur()).isEqualTo(commentairesEvaluateur);
      assertThat(aide.getMontantApprouve()).isEqualTo(montantApprouve);
      assertThat(aide.getDateApprobation()).isEqualTo(dateApprobation);
      assertThat(aide.getMembreAidantId()).isEqualTo(membreAidantId);
      assertThat(aide.getNomAidant()).isEqualTo(nomAidant);
      assertThat(aide.getDateDebutAide()).isEqualTo(dateDebutAide);
      assertThat(aide.getDateFinAide()).isEqualTo(dateFinAide);
      assertThat(aide.getMontantVerse()).isEqualTo(montantVerse);
      assertThat(aide.getModeVersement()).isEqualTo(modeVersement);
      assertThat(aide.getNumeroTransaction()).isEqualTo(numeroTransaction);
      assertThat(aide.getDateVersement()).isEqualTo(dateVersement);
    }

    @Test
    @DisplayName("Test tous les getters/setters - Partie 3")
    void testTousLesGettersSettersPart3() {
      // Données de test
      String commentairesBeneficiaire = "Merci beaucoup pour cette aide";
      Integer noteSatisfaction = 5;
      Boolean aidePublique = false;
      Boolean aideAnonyme = true;
      Integer nombreVues = 25;
      String raisonRejet = "Dossier incomplet";
      LocalDateTime dateRejet = LocalDateTime.now();
      UUID rejeteParId = UUID.randomUUID();
      String rejetePar = "Admin System";

      // Test des setters
      aide.setCommentairesBeneficiaire(commentairesBeneficiaire);
      aide.setNoteSatisfaction(noteSatisfaction);
      aide.setAidePublique(aidePublique);
      aide.setAideAnonyme(aideAnonyme);
      aide.setNombreVues(nombreVues);
      aide.setRaisonRejet(raisonRejet);
      aide.setDateRejet(dateRejet);
      aide.setRejeteParId(rejeteParId);
      aide.setRejetePar(rejetePar);

      // Test des getters
      assertThat(aide.getCommentairesBeneficiaire()).isEqualTo(commentairesBeneficiaire);
      assertThat(aide.getNoteSatisfaction()).isEqualTo(noteSatisfaction);
      assertThat(aide.getAidePublique()).isEqualTo(aidePublique);
      assertThat(aide.getAideAnonyme()).isEqualTo(aideAnonyme);
      assertThat(aide.getNombreVues()).isEqualTo(nombreVues);
      assertThat(aide.getRaisonRejet()).isEqualTo(raisonRejet);
      assertThat(aide.getDateRejet()).isEqualTo(dateRejet);
      assertThat(aide.getRejeteParId()).isEqualTo(rejeteParId);
      assertThat(aide.getRejetePar()).isEqualTo(rejetePar);
    }
  }

  @Nested
  @DisplayName("Tests Méthodes Métier")
  class MethodesMetierTests {

    @Test
    @DisplayName("Test méthodes de statut")
    void testMethodesStatut() {
      // Test isEnAttente
      aide.setStatut("EN_ATTENTE");
      assertThat(aide.isEnAttente()).isTrue();

      aide.setStatut("APPROUVEE");
      assertThat(aide.isEnAttente()).isFalse();

      // Test isApprouvee
      aide.setStatut("APPROUVEE");
      assertThat(aide.isApprouvee()).isTrue();

      aide.setStatut("REJETEE");
      assertThat(aide.isApprouvee()).isFalse();

      // Test isRejetee
      aide.setStatut("REJETEE");
      assertThat(aide.isRejetee()).isTrue();

      aide.setStatut("EN_ATTENTE");
      assertThat(aide.isRejetee()).isFalse();

      // Test isTerminee
      aide.setStatut("TERMINEE");
      assertThat(aide.isTerminee()).isTrue();

      aide.setStatut("EN_COURS_AIDE");
      assertThat(aide.isTerminee()).isFalse();
    }

    @Test
    @DisplayName("Test méthodes de libellé")
    void testMethodesLibelle() {
      // Test getTypeAideLibelle
      aide.setTypeAide("FINANCIERE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide Financière");

      aide.setTypeAide("MEDICALE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide Médicale");

      aide.setTypeAide(null);
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Non défini");

      // Test getStatutLibelle
      aide.setStatut("EN_ATTENTE");
      assertThat(aide.getStatutLibelle()).isEqualTo("En Attente");

      aide.setStatut("APPROUVEE");
      assertThat(aide.getStatutLibelle()).isEqualTo("Approuvée");

      aide.setStatut(null);
      assertThat(aide.getStatutLibelle()).isEqualTo("Non défini");

      // Test getPrioriteLibelle
      aide.setPriorite("URGENTE");
      assertThat(aide.getPrioriteLibelle()).isEqualTo("Urgente");

      aide.setPriorite("HAUTE");
      assertThat(aide.getPrioriteLibelle()).isEqualTo("Haute");

      aide.setPriorite(null);
      assertThat(aide.getPrioriteLibelle()).isEqualTo("Normale");
    }

    @Test
    @DisplayName("Test méthodes de calcul")
    void testMethodesCalcul() {
      // Test getPourcentageApprobation
      aide.setMontantDemande(new BigDecimal("1000.00"));
      aide.setMontantApprouve(new BigDecimal("800.00"));
      assertThat(aide.getPourcentageApprobation()).isEqualTo(80);

      // Test avec montant demandé null
      aide.setMontantDemande(null);
      assertThat(aide.getPourcentageApprobation()).isEqualTo(0);

      // Test getEcartMontant
      aide.setMontantDemande(new BigDecimal("1000.00"));
      aide.setMontantApprouve(new BigDecimal("800.00"));
      assertThat(aide.getEcartMontant()).isEqualTo(new BigDecimal("200.00"));

      // Test avec montants null
      aide.setMontantDemande(null);
      aide.setMontantApprouve(null);
      assertThat(aide.getEcartMontant()).isEqualTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("Test méthodes métier")
    void testMethodesMetier() {
      // Test approuver
      UUID evaluateurId = UUID.randomUUID();
      String nomEvaluateur = "Marie Martin";
      BigDecimal montantApprouve = new BigDecimal("800.00");
      String commentaires = "Dossier approuvé";

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

      assertThat(aide.getStatut()).isEqualTo("APPROUVEE");
      assertThat(aide.getMembreEvaluateurId()).isEqualTo(evaluateurId);
      assertThat(aide.getNomEvaluateur()).isEqualTo(nomEvaluateur);
      assertThat(aide.getMontantApprouve()).isEqualTo(montantApprouve);
      assertThat(aide.getCommentairesEvaluateur()).isEqualTo(commentaires);
      assertThat(aide.getDateEvaluation()).isNotNull();
      assertThat(aide.getDateApprobation()).isNotNull();

      // Test rejeter
      aide.setStatut("EN_ATTENTE"); // Reset
      UUID rejeteurId = UUID.randomUUID();
      String nomRejeteur = "Paul Durand";
      String raisonRejet = "Dossier incomplet";

      aide.rejeter(rejeteurId, nomRejeteur, raisonRejet);

      assertThat(aide.getStatut()).isEqualTo("REJETEE");
      assertThat(aide.getRejeteParId()).isEqualTo(rejeteurId);
      assertThat(aide.getRejetePar()).isEqualTo(nomRejeteur);
      assertThat(aide.getRaisonRejet()).isEqualTo(raisonRejet);
      assertThat(aide.getDateRejet()).isNotNull();

      // Test demarrerAide
      aide.setStatut("APPROUVEE"); // Reset
      UUID aidantId = UUID.randomUUID();
      String nomAidant = "Jean Dupont";

      aide.demarrerAide(aidantId, nomAidant);

      assertThat(aide.getStatut()).isEqualTo("EN_COURS_AIDE");
      assertThat(aide.getMembreAidantId()).isEqualTo(aidantId);
      assertThat(aide.getNomAidant()).isEqualTo(nomAidant);
      assertThat(aide.getDateDebutAide()).isNotNull();

      // Test terminerAvecVersement
      BigDecimal montantVerse = new BigDecimal("800.00");
      String modeVersement = "WAVE_MONEY";
      String numeroTransaction = "TXN123456789";

      aide.terminerAvecVersement(montantVerse, modeVersement, numeroTransaction);

      assertThat(aide.getStatut()).isEqualTo("TERMINEE");
      assertThat(aide.getMontantVerse()).isEqualTo(montantVerse);
      assertThat(aide.getModeVersement()).isEqualTo(modeVersement);
      assertThat(aide.getNumeroTransaction()).isEqualTo(numeroTransaction);
      assertThat(aide.getDateVersement()).isNotNull();
      assertThat(aide.getDateFinAide()).isNotNull();

      // Test incrementerVues
      aide.setNombreVues(null);
      aide.incrementerVues();
      assertThat(aide.getNombreVues()).isEqualTo(1);

      aide.incrementerVues();
      assertThat(aide.getNombreVues()).isEqualTo(2);
    }

    @Test
    @DisplayName("Test méthodes métier complémentaires")
    void testMethodesMetierComplementaires() {
      // Test tous les statuts
      aide.setStatut("EN_COURS_EVALUATION");
      assertThat(aide.isEnCoursEvaluation()).isTrue();
      assertThat(aide.isEnAttente()).isFalse();

      aide.setStatut("EN_COURS_AIDE");
      assertThat(aide.isEnCoursAide()).isTrue();
      assertThat(aide.isTerminee()).isFalse();

      aide.setStatut("ANNULEE");
      assertThat(aide.isAnnulee()).isTrue();

      // Test priorité urgente
      aide.setPriorite("URGENTE");
      assertThat(aide.isUrgente()).isTrue();

      aide.setPriorite("NORMALE");
      assertThat(aide.isUrgente()).isFalse();

      // Test date limite
      aide.setDateLimite(LocalDate.now().plusDays(5));
      assertThat(aide.isDateLimiteDepassee()).isFalse();
      assertThat(aide.getJoursRestants()).isEqualTo(5);

      aide.setDateLimite(LocalDate.now().minusDays(3));
      assertThat(aide.isDateLimiteDepassee()).isTrue();
      assertThat(aide.getJoursRestants()).isEqualTo(0);

      // Test avec date limite null
      aide.setDateLimite(null);
      assertThat(aide.isDateLimiteDepassee()).isFalse();
      assertThat(aide.getJoursRestants()).isEqualTo(0);

      // Test aide financière
      aide.setTypeAide("FINANCIERE");
      aide.setMontantDemande(new BigDecimal("50000.00"));
      assertThat(aide.isAideFinanciere()).isTrue();

      aide.setMontantDemande(null);
      assertThat(aide.isAideFinanciere()).isFalse();

      aide.setTypeAide("MATERIELLE");
      aide.setMontantDemande(new BigDecimal("50000.00"));
      assertThat(aide.isAideFinanciere()).isFalse();

      // Test getEcartMontant avec différents cas
      aide.setMontantDemande(new BigDecimal("100000.00"));
      aide.setMontantApprouve(new BigDecimal("80000.00"));
      assertThat(aide.getEcartMontant()).isEqualByComparingTo(new BigDecimal("20000.00"));

      // Test avec montantDemande null
      aide.setMontantDemande(null);
      aide.setMontantApprouve(new BigDecimal("80000.00"));
      assertThat(aide.getEcartMontant()).isEqualByComparingTo(BigDecimal.ZERO);

      // Test avec montantApprouve null
      aide.setMontantDemande(new BigDecimal("100000.00"));
      aide.setMontantApprouve(null);
      assertThat(aide.getEcartMontant()).isEqualByComparingTo(BigDecimal.ZERO);

      // Test avec les deux null
      aide.setMontantDemande(null);
      aide.setMontantApprouve(null);
      assertThat(aide.getEcartMontant()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("Test libellés complets")
    void testLibellesComplets() {
      // Test tous les types d'aide
      aide.setTypeAide("MATERIELLE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide Matérielle");

      aide.setTypeAide("LOGEMENT");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide au Logement");

      aide.setTypeAide("MEDICALE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide Médicale");

      aide.setTypeAide("JURIDIQUE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide Juridique");

      aide.setTypeAide("EDUCATION");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Aide à l'Éducation");

      aide.setTypeAide("SANTE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("SANTE"); // Valeur par défaut car non définie dans le switch

      aide.setTypeAide("AUTRE");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("Autre");

      aide.setTypeAide("TYPE_INCONNU");
      assertThat(aide.getTypeAideLibelle()).isEqualTo("TYPE_INCONNU");

      // Test tous les statuts
      aide.setStatut("EN_COURS_EVALUATION");
      assertThat(aide.getStatutLibelle()).isEqualTo("En Cours d'Évaluation");

      aide.setStatut("REJETEE");
      assertThat(aide.getStatutLibelle()).isEqualTo("Rejetée");

      aide.setStatut("EN_COURS_AIDE");
      assertThat(aide.getStatutLibelle()).isEqualTo("En Cours d'Aide");

      aide.setStatut("TERMINEE");
      assertThat(aide.getStatutLibelle()).isEqualTo("Terminée");

      aide.setStatut("ANNULEE");
      assertThat(aide.getStatutLibelle()).isEqualTo("Annulée");

      aide.setStatut("STATUT_INCONNU");
      assertThat(aide.getStatutLibelle()).isEqualTo("STATUT_INCONNU");

      // Test toutes les priorités
      aide.setPriorite("BASSE");
      assertThat(aide.getPrioriteLibelle()).isEqualTo("Basse");

      aide.setPriorite("NORMALE");
      assertThat(aide.getPrioriteLibelle()).isEqualTo("Normale");

      aide.setPriorite("HAUTE");
      assertThat(aide.getPrioriteLibelle()).isEqualTo("Haute");

      aide.setPriorite("PRIORITE_INCONNUE");
      assertThat(aide.getPrioriteLibelle()).isEqualTo("PRIORITE_INCONNUE");
    }

    @Test
    @DisplayName("Test constructeur avec paramètres")
    void testConstructeurAvecParametres() {
      UUID membreDemandeurId = UUID.randomUUID();
      UUID associationId = UUID.randomUUID();
      String typeAide = "FINANCIERE";
      String titre = "Aide médicale urgente";

      AideDTO nouvelleAide = new AideDTO(membreDemandeurId, associationId, typeAide, titre);

      assertThat(nouvelleAide.getMembreDemandeurId()).isEqualTo(membreDemandeurId);
      assertThat(nouvelleAide.getAssociationId()).isEqualTo(associationId);
      assertThat(nouvelleAide.getTypeAide()).isEqualTo(typeAide);
      assertThat(nouvelleAide.getTitre()).isEqualTo(titre);
      assertThat(nouvelleAide.getNumeroReference()).isNotNull();
      assertThat(nouvelleAide.getNumeroReference()).startsWith("AIDE-");
      // Vérifier les valeurs par défaut
      assertThat(nouvelleAide.getStatut()).isEqualTo("EN_ATTENTE");
      assertThat(nouvelleAide.getPriorite()).isEqualTo("NORMALE");
      assertThat(nouvelleAide.getDevise()).isEqualTo("XOF");
      assertThat(nouvelleAide.getJustificatifsFournis()).isFalse();
      assertThat(nouvelleAide.getAidePublique()).isTrue();
      assertThat(nouvelleAide.getAideAnonyme()).isFalse();
      assertThat(nouvelleAide.getNombreVues()).isEqualTo(0);
    }
  }

  @Test
  @DisplayName("Test toString complet")
  void testToStringComplet() {
    aide.setNumeroReference("AIDE-2025-ABC123");
    aide.setTitre("Aide médicale");
    aide.setStatut("EN_ATTENTE");
    aide.setTypeAide("FINANCIERE");
    aide.setMontantDemande(new BigDecimal("100000.00"));
    aide.setPriorite("URGENTE");

    String result = aide.toString();
    assertThat(result).isNotNull();
    assertThat(result).contains("AideDTO");
    assertThat(result).contains("numeroReference='AIDE-2025-ABC123'");
    assertThat(result).contains("typeAide='FINANCIERE'");
    assertThat(result).contains("titre='Aide médicale'");
    assertThat(result).contains("statut='EN_ATTENTE'");
    assertThat(result).contains("montantDemande=100000.00");
    assertThat(result).contains("priorite='URGENTE'");
  }
}
