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

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

import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
import java.time.LocalDate;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

@DisplayName("Tests pour MembreDTO")
class MembreDTOTest {

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

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

    @Test
    @DisplayName("Constructeur par défaut initialise correctement")
    void testConstructeurParDefaut() {
      MembreDTO membre = new MembreDTO();

      assertThat(membre.getStatut()).isEqualTo(StatutMembre.ACTIF);
      assertThat(membre.getDateAdhesion()).isNotNull();
      assertThat(membre.getMembreBureau()).isFalse();
      assertThat(membre.getResponsable()).isFalse();
    }

    @Test
    @DisplayName("Constructeur avec paramètres essentiels")
    void testConstructeurAvecParametres() {
      MembreDTO membre = new MembreDTO("UF-2025-000001", "Dupont", "Jean", "jean@example.com");

      assertThat(membre.getNumeroMembre()).isEqualTo("UF-2025-000001");
      assertThat(membre.getNom()).isEqualTo("Dupont");
      assertThat(membre.getPrenom()).isEqualTo("Jean");
      assertThat(membre.getEmail()).isEqualTo("jean@example.com");
      assertThat(membre.getStatut()).isEqualTo(StatutMembre.ACTIF);
    }
  }

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

    @Test
    @DisplayName("Tous les getters et setters fonctionnent")
    void testGettersSetters() {
      MembreDTO membre = new MembreDTO();
      UUID associationId = UUID.randomUUID();

      membre.setNumeroMembre("UF-2025-000002");
      membre.setNom("Martin");
      membre.setPrenom("Marie");
      membre.setEmail("marie@example.com");
      membre.setTelephone("+221771234567");
      membre.setDateNaissance(LocalDate.of(1990, 5, 15));
      membre.setAdresse("123 Rue Example");
      membre.setProfession("Ingénieur");
      membre.setStatutMatrimonial("Célibataire");
      membre.setNationalite("Sénégalaise");
      membre.setNumeroIdentite("123456789");
      membre.setTypeIdentite("CNI");
      membre.setStatut(StatutMembre.ACTIF);
      membre.setAssociationId(associationId);
      membre.setAssociationNom("Association Test");
      membre.setDateAdhesion(LocalDate.of(2024, 1, 1));
      membre.setRegion("Dakar");
      membre.setVille("Dakar");
      membre.setQuartier("Almadies");
      membre.setRole("PRESIDENT");
      membre.setMembreBureau(true);
      membre.setResponsable(true);
      membre.setPhotoUrl("http://example.com/photo.jpg");

      assertThat(membre.getNumeroMembre()).isEqualTo("UF-2025-000002");
      assertThat(membre.getNom()).isEqualTo("Martin");
      assertThat(membre.getPrenom()).isEqualTo("Marie");
      assertThat(membre.getEmail()).isEqualTo("marie@example.com");
      assertThat(membre.getTelephone()).isEqualTo("+221771234567");
      assertThat(membre.getDateNaissance()).isEqualTo(LocalDate.of(1990, 5, 15));
      assertThat(membre.getAdresse()).isEqualTo("123 Rue Example");
      assertThat(membre.getProfession()).isEqualTo("Ingénieur");
      assertThat(membre.getStatutMatrimonial()).isEqualTo("Célibataire");
      assertThat(membre.getNationalite()).isEqualTo("Sénégalaise");
      assertThat(membre.getNumeroIdentite()).isEqualTo("123456789");
      assertThat(membre.getTypeIdentite()).isEqualTo("CNI");
      assertThat(membre.getStatut()).isEqualTo(StatutMembre.ACTIF);
      assertThat(membre.getAssociationId()).isEqualTo(associationId);
      assertThat(membre.getAssociationNom()).isEqualTo("Association Test");
      assertThat(membre.getDateAdhesion()).isEqualTo(LocalDate.of(2024, 1, 1));
      assertThat(membre.getRegion()).isEqualTo("Dakar");
      assertThat(membre.getVille()).isEqualTo("Dakar");
      assertThat(membre.getQuartier()).isEqualTo("Almadies");
      assertThat(membre.getRole()).isEqualTo("PRESIDENT");
      assertThat(membre.getMembreBureau()).isTrue();
      assertThat(membre.getResponsable()).isTrue();
      assertThat(membre.getPhotoUrl()).isEqualTo("http://example.com/photo.jpg");
    }
  }

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

    @Test
    @DisplayName("getNomComplet avec prénom et nom")
    void testGetNomCompletAvecPrenomEtNom() {
      MembreDTO membre = new MembreDTO();
      membre.setPrenom("Jean");
      membre.setNom("Dupont");

      assertThat(membre.getNomComplet()).isEqualTo("Jean Dupont");
    }

    @Test
    @DisplayName("getNomComplet avec seulement nom")
    void testGetNomCompletAvecSeulementNom() {
      MembreDTO membre = new MembreDTO();
      membre.setNom("Dupont");

      assertThat(membre.getNomComplet()).isEqualTo("Dupont");
    }

    @Test
    @DisplayName("getNomComplet avec seulement prénom")
    void testGetNomCompletAvecSeulementPrenom() {
      MembreDTO membre = new MembreDTO();
      membre.setPrenom("Jean");

      assertThat(membre.getNomComplet()).isEqualTo("Jean");
    }

    @Test
    @DisplayName("getNomComplet avec valeurs null")
    void testGetNomCompletAvecNull() {
      MembreDTO membre = new MembreDTO();

      assertThat(membre.getNomComplet()).isEmpty();
    }

    @Test
    @DisplayName("estMajeur - membre majeur")
    void testEstMajeur() {
      MembreDTO membre = new MembreDTO();
      membre.setDateNaissance(LocalDate.now().minusYears(25));

      assertThat(membre.estMajeur()).isTrue();
    }

    @Test
    @DisplayName("estMajeur - membre mineur")
    void testEstMajeurMineur() {
      MembreDTO membre = new MembreDTO();
      membre.setDateNaissance(LocalDate.now().minusYears(15));

      assertThat(membre.estMajeur()).isFalse();
    }

    @Test
    @DisplayName("estMajeur - exactement 18 ans")
    void testEstMajeurExactement18Ans() {
      MembreDTO membre = new MembreDTO();
      membre.setDateNaissance(LocalDate.now().minusYears(18));

      assertThat(membre.estMajeur()).isTrue();
    }

    @Test
    @DisplayName("estMajeur - date de naissance null")
    void testEstMajeurDateNaissanceNull() {
      MembreDTO membre = new MembreDTO();

      assertThat(membre.estMajeur()).isFalse();
    }

    @Test
    @DisplayName("getAge - avec date de naissance")
    void testGetAge() {
      MembreDTO membre = new MembreDTO();
      membre.setDateNaissance(LocalDate.now().minusYears(30));

      int age = membre.getAge();
      assertThat(age).isBetween(29, 31); // Tolérance d'un an pour les tests
    }

    @Test
    @DisplayName("getAge - date de naissance null")
    void testGetAgeDateNaissanceNull() {
      MembreDTO membre = new MembreDTO();

      assertThat(membre.getAge()).isEqualTo(-1);
    }

    @Test
    @DisplayName("estActif - statut ACTIF")
    void testEstActif() {
      MembreDTO membre = new MembreDTO();
      membre.setStatut(StatutMembre.ACTIF);

      assertThat(membre.estActif()).isTrue();
    }

    @Test
    @DisplayName("estActif - statut INACTIF")
    void testEstActifInactif() {
      MembreDTO membre = new MembreDTO();
      membre.setStatut(StatutMembre.INACTIF);

      assertThat(membre.estActif()).isFalse();
    }

    @Test
    @DisplayName("hasRoleDirection - membre du bureau")
    void testHasRoleDirectionMembreBureau() {
      MembreDTO membre = new MembreDTO();
      membre.setMembreBureau(true);

      assertThat(membre.hasRoleDirection()).isTrue();
    }

    @Test
    @DisplayName("hasRoleDirection - responsable")
    void testHasRoleDirectionResponsable() {
      MembreDTO membre = new MembreDTO();
      membre.setResponsable(true);

      assertThat(membre.hasRoleDirection()).isTrue();
    }

    @Test
    @DisplayName("hasRoleDirection - ni membre bureau ni responsable")
    void testHasRoleDirectionAucun() {
      MembreDTO membre = new MembreDTO();
      membre.setMembreBureau(false);
      membre.setResponsable(false);

      assertThat(membre.hasRoleDirection()).isFalse();
    }

    @Test
    @DisplayName("getStatutLibelle - avec statut")
    void testGetStatutLibelle() {
      MembreDTO membre = new MembreDTO();
      membre.setStatut(StatutMembre.ACTIF);

      assertThat(membre.getStatutLibelle()).isEqualTo(StatutMembre.ACTIF.getLibelle());
    }

    @Test
    @DisplayName("getStatutLibelle - statut null")
    void testGetStatutLibelleNull() {
      MembreDTO membre = new MembreDTO();
      membre.setStatut(null);

      assertThat(membre.getStatutLibelle()).isEqualTo("Non défini");
    }

    @Test
    @DisplayName("sontDonneesValides - données valides")
    void testSontDonneesValides() {
      MembreDTO membre = new MembreDTO();
      membre.setNumeroMembre("UF-2025-000001");
      membre.setNom("Dupont");
      membre.setPrenom("Jean");
      membre.setStatut(StatutMembre.ACTIF);
      membre.setAssociationId(UUID.randomUUID());

      assertThat(membre.sontDonneesValides()).isTrue();
    }

    @Test
    @DisplayName("sontDonneesValides - numéro membre manquant")
    void testSontDonneesValidesNumeroManquant() {
      MembreDTO membre = new MembreDTO();
      membre.setNom("Dupont");
      membre.setPrenom("Jean");
      membre.setStatut(StatutMembre.ACTIF);
      membre.setAssociationId(UUID.randomUUID());

      assertThat(membre.sontDonneesValides()).isFalse();
    }

    @Test
    @DisplayName("sontDonneesValides - nom manquant")
    void testSontDonneesValidesNomManquant() {
      MembreDTO membre = new MembreDTO();
      membre.setNumeroMembre("UF-2025-000001");
      membre.setPrenom("Jean");
      membre.setStatut(StatutMembre.ACTIF);
      membre.setAssociationId(UUID.randomUUID());

      assertThat(membre.sontDonneesValides()).isFalse();
    }

    @Test
    @DisplayName("sontDonneesValides - prénom manquant")
    void testSontDonneesValidesPrenomManquant() {
      MembreDTO membre = new MembreDTO();
      membre.setNumeroMembre("UF-2025-000001");
      membre.setNom("Dupont");
      membre.setStatut(StatutMembre.ACTIF);
      membre.setAssociationId(UUID.randomUUID());

      assertThat(membre.sontDonneesValides()).isFalse();
    }

    @Test
    @DisplayName("sontDonneesValides - statut null")
    void testSontDonneesValidesStatutNull() {
      MembreDTO membre = new MembreDTO();
      membre.setNumeroMembre("UF-2025-000001");
      membre.setNom("Dupont");
      membre.setPrenom("Jean");
      membre.setAssociationId(UUID.randomUUID());
      membre.setStatut(null); // Forcer null car le constructeur par défaut initialise avec ACTIF

      assertThat(membre.sontDonneesValides()).isFalse();
    }

    @Test
    @DisplayName("sontDonneesValides - associationId null")
    void testSontDonneesValidesAssociationIdNull() {
      MembreDTO membre = new MembreDTO();
      membre.setNumeroMembre("UF-2025-000001");
      membre.setNom("Dupont");
      membre.setPrenom("Jean");
      membre.setStatut(StatutMembre.ACTIF);

      assertThat(membre.sontDonneesValides()).isFalse();
    }
  }

  @Nested
  @DisplayName("Tests toString")
  class ToStringTests {

    @Test
    @DisplayName("toString contient les informations essentielles")
    void testToString() {
      MembreDTO membre = new MembreDTO();
      membre.setNumeroMembre("UF-2025-000001");
      membre.setNom("Dupont");
      membre.setPrenom("Jean");
      membre.setEmail("jean@example.com");
      membre.setStatut(StatutMembre.ACTIF);
      UUID associationId = UUID.randomUUID();
      membre.setAssociationId(associationId);
      membre.setDateAdhesion(LocalDate.of(2024, 1, 1));

      String toString = membre.toString();

      assertThat(toString).contains("MembreDTO");
      assertThat(toString).contains("UF-2025-000001");
      assertThat(toString).contains("Dupont");
      assertThat(toString).contains("Jean");
      assertThat(toString).contains("jean@example.com");
      assertThat(toString).contains(associationId.toString());
    }
  }
}

