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

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

import java.time.LocalDate;
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.ValueSource;

@DisplayName("Tests pour BeneficiaireAideDTO")
class BeneficiaireAideDTOTest {

  @Test
  @DisplayName("Test de base - classe peut être instanciée")
  void testClasseInstanciable() {
    BeneficiaireAideDTO dto = new BeneficiaireAideDTO();
    assertThat(dto).isNotNull();
  }

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

    @Test
    @DisplayName("Constructeur par défaut")
    void testConstructeurParDefaut() {
      BeneficiaireAideDTO dto = new BeneficiaireAideDTO();

      assertThat(dto).isNotNull();
      assertThat(dto.getEstDemandeurPrincipal()).isFalse();
    }

    @Test
    @DisplayName("Setters avec tous les paramètres")
    void testSettersAvecTousLesParametres() {
      BeneficiaireAideDTO dto = new BeneficiaireAideDTO();
      dto.setId("id-123");
      dto.setNomComplet("Jean Dupont");
      dto.setRelationDemandeur("Lui-même");
      dto.setDateNaissance(LocalDate.of(1990, 1, 1));
      dto.setAge(35);
      dto.setGenre("M");
      dto.setTelephone("+221771234567");
      dto.setEmail("jean@example.com");
      dto.setAdresse("123 Rue Test");
      dto.setSituationParticuliere("Handicap visuel");
      dto.setEstDemandeurPrincipal(true);
      dto.setPourcentageAide(100.0);
      dto.setMontantSpecifique(50000.0);

      assertThat(dto.getId()).isEqualTo("id-123");
      assertThat(dto.getNomComplet()).isEqualTo("Jean Dupont");
      assertThat(dto.getRelationDemandeur()).isEqualTo("Lui-même");
      assertThat(dto.getDateNaissance()).isEqualTo(LocalDate.of(1990, 1, 1));
      assertThat(dto.getAge()).isEqualTo(35);
      assertThat(dto.getGenre()).isEqualTo("M");
      assertThat(dto.getTelephone()).isEqualTo("+221771234567");
      assertThat(dto.getEmail()).isEqualTo("jean@example.com");
      assertThat(dto.getAdresse()).isEqualTo("123 Rue Test");
      assertThat(dto.getSituationParticuliere()).isEqualTo("Handicap visuel");
      assertThat(dto.getEstDemandeurPrincipal()).isTrue();
      assertThat(dto.getPourcentageAide()).isEqualTo(100.0);
      assertThat(dto.getMontantSpecifique()).isEqualTo(50000.0);
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      BeneficiaireAideDTO dto = BeneficiaireAideDTO.builder()
          .id("id-456")
          .nomComplet("Marie Martin")
          .relationDemandeur("Épouse")
          .dateNaissance(LocalDate.of(1985, 5, 15))
          .age(40)
          .genre("F")
          .telephone("+221771234568")
          .email("marie@example.com")
          .adresse("456 Rue Autre")
          .situationParticuliere("Maladie chronique")
          .estDemandeurPrincipal(false)
          .pourcentageAide(50.0)
          .montantSpecifique(25000.0)
          .build();

      assertThat(dto.getId()).isEqualTo("id-456");
      assertThat(dto.getNomComplet()).isEqualTo("Marie Martin");
      assertThat(dto.getRelationDemandeur()).isEqualTo("Épouse");
      assertThat(dto.getDateNaissance()).isEqualTo(LocalDate.of(1985, 5, 15));
      assertThat(dto.getAge()).isEqualTo(40);
      assertThat(dto.getGenre()).isEqualTo("F");
      assertThat(dto.getTelephone()).isEqualTo("+221771234568");
      assertThat(dto.getEmail()).isEqualTo("marie@example.com");
      assertThat(dto.getAdresse()).isEqualTo("456 Rue Autre");
      assertThat(dto.getSituationParticuliere()).isEqualTo("Maladie chronique");
      assertThat(dto.getEstDemandeurPrincipal()).isFalse();
      assertThat(dto.getPourcentageAide()).isEqualTo(50.0);
      assertThat(dto.getMontantSpecifique()).isEqualTo(25000.0);
    }
  }

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

    @Test
    @DisplayName("Test tous les getters/setters")
    void testTousLesGettersSetters() {
      BeneficiaireAideDTO dto = new BeneficiaireAideDTO();

      dto.setId("id-789");
      dto.setNomComplet("Pierre Durand");
      dto.setRelationDemandeur("Fils");
      dto.setDateNaissance(LocalDate.of(2010, 3, 20));
      dto.setAge(15);
      dto.setGenre("M");
      dto.setTelephone("+221771234569");
      dto.setEmail("pierre@example.com");
      dto.setAdresse("789 Rue Encore");
      dto.setSituationParticuliere("Étudiant");
      dto.setEstDemandeurPrincipal(false);
      dto.setPourcentageAide(30.0);
      dto.setMontantSpecifique(15000.0);

      assertThat(dto.getId()).isEqualTo("id-789");
      assertThat(dto.getNomComplet()).isEqualTo("Pierre Durand");
      assertThat(dto.getRelationDemandeur()).isEqualTo("Fils");
      assertThat(dto.getDateNaissance()).isEqualTo(LocalDate.of(2010, 3, 20));
      assertThat(dto.getAge()).isEqualTo(15);
      assertThat(dto.getGenre()).isEqualTo("M");
      assertThat(dto.getTelephone()).isEqualTo("+221771234569");
      assertThat(dto.getEmail()).isEqualTo("pierre@example.com");
      assertThat(dto.getAdresse()).isEqualTo("789 Rue Encore");
      assertThat(dto.getSituationParticuliere()).isEqualTo("Étudiant");
      assertThat(dto.getEstDemandeurPrincipal()).isFalse();
      assertThat(dto.getPourcentageAide()).isEqualTo(30.0);
      assertThat(dto.getMontantSpecifique()).isEqualTo(15000.0);
    }

    @Test
    @DisplayName("estDemandeurPrincipal - valeur par défaut")
    void testEstDemandeurPrincipalParDefaut() {
      BeneficiaireAideDTO dto = BeneficiaireAideDTO.builder().build();
      assertThat(dto.getEstDemandeurPrincipal()).isFalse();
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("estDemandeurPrincipal - toutes les valeurs")
    void testEstDemandeurPrincipal(Boolean valeur) {
      BeneficiaireAideDTO dto = BeneficiaireAideDTO.builder()
          .estDemandeurPrincipal(valeur)
          .build();
      assertThat(dto.getEstDemandeurPrincipal()).isEqualTo(valeur);
    }

    @Test
    @DisplayName("Test champs null")
    void testChampsNull() {
      BeneficiaireAideDTO dto = new BeneficiaireAideDTO();

      dto.setId(null);
      dto.setNomComplet(null);
      dto.setRelationDemandeur(null);
      dto.setDateNaissance(null);
      dto.setAge(null);
      dto.setGenre(null);
      dto.setTelephone(null);
      dto.setEmail(null);
      dto.setAdresse(null);
      dto.setSituationParticuliere(null);
      dto.setEstDemandeurPrincipal(null);
      dto.setPourcentageAide(null);
      dto.setMontantSpecifique(null);

      assertThat(dto.getId()).isNull();
      assertThat(dto.getNomComplet()).isNull();
      assertThat(dto.getRelationDemandeur()).isNull();
      assertThat(dto.getDateNaissance()).isNull();
      assertThat(dto.getAge()).isNull();
      assertThat(dto.getGenre()).isNull();
      assertThat(dto.getTelephone()).isNull();
      assertThat(dto.getEmail()).isNull();
      assertThat(dto.getAdresse()).isNull();
      assertThat(dto.getSituationParticuliere()).isNull();
      assertThat(dto.getEstDemandeurPrincipal()).isNull();
      assertThat(dto.getPourcentageAide()).isNull();
      assertThat(dto.getMontantSpecifique()).isNull();
    }
  }

  @Nested
  @DisplayName("Tests equals/hashCode/toString")
  class EqualsHashCodeToStringTests {

    @Test
    @DisplayName("equals - même objet")
    void testEqualsMemeObjet() {
      BeneficiaireAideDTO dto = new BeneficiaireAideDTO();
      assertThat(dto).isEqualTo(dto);
    }

    @Test
    @DisplayName("equals - objets égaux")
    void testEqualsObjetsEgaux() {
      LocalDate dateNaissance = LocalDate.of(1990, 1, 1);
      BeneficiaireAideDTO dto1 = BeneficiaireAideDTO.builder()
          .id("id-1")
          .nomComplet("Jean Dupont")
          .relationDemandeur("Lui-même")
          .dateNaissance(dateNaissance)
          .age(35)
          .genre("M")
          .telephone("+221771234567")
          .email("jean@example.com")
          .adresse("123 Rue Test")
          .situationParticuliere("Handicap visuel")
          .estDemandeurPrincipal(true)
          .pourcentageAide(100.0)
          .montantSpecifique(50000.0)
          .build();
      BeneficiaireAideDTO dto2 = BeneficiaireAideDTO.builder()
          .id("id-1")
          .nomComplet("Jean Dupont")
          .relationDemandeur("Lui-même")
          .dateNaissance(dateNaissance)
          .age(35)
          .genre("M")
          .telephone("+221771234567")
          .email("jean@example.com")
          .adresse("123 Rue Test")
          .situationParticuliere("Handicap visuel")
          .estDemandeurPrincipal(true)
          .pourcentageAide(100.0)
          .montantSpecifique(50000.0)
          .build();

      assertThat(dto1).isEqualTo(dto2);
      assertThat(dto1.hashCode()).isEqualTo(dto2.hashCode());
    }

    @Test
    @DisplayName("equals - objets différents")
    void testEqualsObjetsDifferents() {
      BeneficiaireAideDTO dto1 = BeneficiaireAideDTO.builder()
          .id("id-1")
          .nomComplet("Test 1")
          .build();
      BeneficiaireAideDTO dto2 = BeneficiaireAideDTO.builder()
          .id("id-2")
          .nomComplet("Test 2")
          .build();

      assertThat(dto1).isNotEqualTo(dto2);
    }

    @Test
    @DisplayName("equals - avec null")
    void testEqualsAvecNull() {
      BeneficiaireAideDTO dto = new BeneficiaireAideDTO();
      assertThat(dto).isNotEqualTo(null);
    }

    @Test
    @DisplayName("hashCode - objets égaux ont même hashCode")
    void testHashCodeObjetsEgaux() {
      LocalDate dateNaissance = LocalDate.of(1990, 1, 1);
      BeneficiaireAideDTO dto1 = BeneficiaireAideDTO.builder()
          .id("id-1")
          .nomComplet("Jean Dupont")
          .dateNaissance(dateNaissance)
          .build();
      BeneficiaireAideDTO dto2 = BeneficiaireAideDTO.builder()
          .id("id-1")
          .nomComplet("Jean Dupont")
          .dateNaissance(dateNaissance)
          .build();

      assertThat(dto1.hashCode()).isEqualTo(dto2.hashCode());
    }

    @Test
    @DisplayName("toString - contient les informations essentielles")
    void testToString() {
      BeneficiaireAideDTO dto = BeneficiaireAideDTO.builder()
          .id("id-123")
          .nomComplet("Jean Dupont")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("BeneficiaireAideDTO");
      assertThat(toString).contains("id-123");
      assertThat(toString).contains("Jean Dupont");
    }

    @Test
    @DisplayName("toString - avec tous les champs")
    void testToStringTousChamps() {
      LocalDate dateNaissance = LocalDate.of(1990, 1, 1);
      BeneficiaireAideDTO dto = BeneficiaireAideDTO.builder()
          .id("id-123")
          .nomComplet("Jean Dupont")
          .relationDemandeur("Lui-même")
          .dateNaissance(dateNaissance)
          .age(35)
          .genre("M")
          .telephone("+221771234567")
          .email("jean@example.com")
          .adresse("123 Rue Test")
          .situationParticuliere("Handicap visuel")
          .estDemandeurPrincipal(true)
          .pourcentageAide(100.0)
          .montantSpecifique(50000.0)
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("BeneficiaireAideDTO");
      assertThat(toString).contains("id-123");
      assertThat(toString).contains("Jean Dupont");
    }
  }
}

