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

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

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.ValueSource;

@DisplayName("Tests pour ContactProposantDTO")
class ContactProposantDTOTest {

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

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

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

      assertThat(dto).isNotNull();
      assertThat(dto.getRencontresPhysiquesPossibles()).isFalse();
      assertThat(dto.getAppelsAcceptes()).isTrue();
      assertThat(dto.getSmsAcceptes()).isTrue();
      assertThat(dto.getEmailsAcceptes()).isTrue();
    }

    @Test
    @DisplayName("Constructeur avec tous les paramètres")
    void testConstructeurAvecParametres() {
      Map<String, String> autresContacts = new HashMap<>();
      autresContacts.put("facebook", "user123");
      List<String> langues = Arrays.asList("fr", "en");

      ContactProposantDTO dto = new ContactProposantDTO(
          "+221771234567",
          "+221771234568",
          "email@example.com",
          "email2@example.com",
          "+221771234569",
          "telegram123",
          autresContacts,
          "123 Rue Test",
          true,
          false,
          true,
          false,
          "9h-17h",
          langues,
          "Instructions spéciales");

      assertThat(dto.getTelephonePrincipal()).isEqualTo("+221771234567");
      assertThat(dto.getTelephoneSecondaire()).isEqualTo("+221771234568");
      assertThat(dto.getEmail()).isEqualTo("email@example.com");
      assertThat(dto.getEmailSecondaire()).isEqualTo("email2@example.com");
      assertThat(dto.getWhatsapp()).isEqualTo("+221771234569");
      assertThat(dto.getTelegram()).isEqualTo("telegram123");
      assertThat(dto.getAutresContacts()).isEqualTo(autresContacts);
      assertThat(dto.getAdressePhysique()).isEqualTo("123 Rue Test");
      assertThat(dto.getRencontresPhysiquesPossibles()).isTrue();
      assertThat(dto.getAppelsAcceptes()).isFalse();
      assertThat(dto.getSmsAcceptes()).isTrue();
      assertThat(dto.getEmailsAcceptes()).isFalse();
      assertThat(dto.getHorairesDisponibilite()).isEqualTo("9h-17h");
      assertThat(dto.getLanguesPreferees()).isEqualTo(langues);
      assertThat(dto.getInstructionsSpeciales()).isEqualTo("Instructions spéciales");
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .whatsapp("+221771234568")
          .adressePhysique("456 Rue Autre")
          .rencontresPhysiquesPossibles(true)
          .appelsAcceptes(true)
          .smsAcceptes(false)
          .emailsAcceptes(true)
          .horairesDisponibilite("10h-18h")
          .languesPreferees(Arrays.asList("fr"))
          .instructionsSpeciales("Pas d'appels le weekend")
          .build();

      assertThat(dto.getTelephonePrincipal()).isEqualTo("+221771234567");
      assertThat(dto.getEmail()).isEqualTo("test@example.com");
      assertThat(dto.getWhatsapp()).isEqualTo("+221771234568");
      assertThat(dto.getAdressePhysique()).isEqualTo("456 Rue Autre");
      assertThat(dto.getRencontresPhysiquesPossibles()).isTrue();
      assertThat(dto.getAppelsAcceptes()).isTrue();
      assertThat(dto.getSmsAcceptes()).isFalse();
      assertThat(dto.getEmailsAcceptes()).isTrue();
      assertThat(dto.getHorairesDisponibilite()).isEqualTo("10h-18h");
      assertThat(dto.getLanguesPreferees()).containsExactly("fr");
      assertThat(dto.getInstructionsSpeciales()).isEqualTo("Pas d'appels le weekend");
    }
  }

  @Nested
  @DisplayName("Tests valeurs par défaut")
  class ValeursParDefautTests {

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("rencontresPhysiquesPossibles - toutes les valeurs")
    void testRencontresPhysiquesPossibles(Boolean valeur) {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .rencontresPhysiquesPossibles(valeur)
          .build();
      assertThat(dto.getRencontresPhysiquesPossibles()).isEqualTo(valeur);
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("appelsAcceptes - toutes les valeurs")
    void testAppelsAcceptes(Boolean valeur) {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .appelsAcceptes(valeur)
          .build();
      assertThat(dto.getAppelsAcceptes()).isEqualTo(valeur);
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("smsAcceptes - toutes les valeurs")
    void testSmsAcceptes(Boolean valeur) {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .smsAcceptes(valeur)
          .build();
      assertThat(dto.getSmsAcceptes()).isEqualTo(valeur);
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("emailsAcceptes - toutes les valeurs")
    void testEmailsAcceptes(Boolean valeur) {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .emailsAcceptes(valeur)
          .build();
      assertThat(dto.getEmailsAcceptes()).isEqualTo(valeur);
    }
  }

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

    @Test
    @DisplayName("Test tous les champs avec setters directs")
    void testTousLesChampsSetters() {
      ContactProposantDTO dto = new ContactProposantDTO();
      Map<String, String> autresContacts = new HashMap<>();
      autresContacts.put("facebook", "user123");
      List<String> langues = Arrays.asList("fr", "en", "ar");

      dto.setTelephonePrincipal("+221771234567");
      dto.setTelephoneSecondaire("+221771234568");
      dto.setEmail("test@example.com");
      dto.setEmailSecondaire("test2@example.com");
      dto.setWhatsapp("+221771234569");
      dto.setTelegram("telegram123");
      dto.setAutresContacts(autresContacts);
      dto.setAdressePhysique("123 Rue Test");
      dto.setRencontresPhysiquesPossibles(true);
      dto.setAppelsAcceptes(false);
      dto.setSmsAcceptes(false);
      dto.setEmailsAcceptes(false);
      dto.setHorairesDisponibilite("9h-17h");
      dto.setLanguesPreferees(langues);
      dto.setInstructionsSpeciales("Instructions spéciales");

      assertThat(dto.getTelephonePrincipal()).isEqualTo("+221771234567");
      assertThat(dto.getTelephoneSecondaire()).isEqualTo("+221771234568");
      assertThat(dto.getEmail()).isEqualTo("test@example.com");
      assertThat(dto.getEmailSecondaire()).isEqualTo("test2@example.com");
      assertThat(dto.getWhatsapp()).isEqualTo("+221771234569");
      assertThat(dto.getTelegram()).isEqualTo("telegram123");
      assertThat(dto.getAutresContacts()).isEqualTo(autresContacts);
      assertThat(dto.getAdressePhysique()).isEqualTo("123 Rue Test");
      assertThat(dto.getRencontresPhysiquesPossibles()).isTrue();
      assertThat(dto.getAppelsAcceptes()).isFalse();
      assertThat(dto.getSmsAcceptes()).isFalse();
      assertThat(dto.getEmailsAcceptes()).isFalse();
      assertThat(dto.getHorairesDisponibilite()).isEqualTo("9h-17h");
      assertThat(dto.getLanguesPreferees()).isEqualTo(langues);
      assertThat(dto.getInstructionsSpeciales()).isEqualTo("Instructions spéciales");
    }

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

      dto.setTelephonePrincipal(null);
      dto.setTelephoneSecondaire(null);
      dto.setEmail(null);
      dto.setEmailSecondaire(null);
      dto.setWhatsapp(null);
      dto.setTelegram(null);
      dto.setAutresContacts(null);
      dto.setAdressePhysique(null);
      dto.setRencontresPhysiquesPossibles(null);
      dto.setAppelsAcceptes(null);
      dto.setSmsAcceptes(null);
      dto.setEmailsAcceptes(null);
      dto.setHorairesDisponibilite(null);
      dto.setLanguesPreferees(null);
      dto.setInstructionsSpeciales(null);

      assertThat(dto.getTelephonePrincipal()).isNull();
      assertThat(dto.getTelephoneSecondaire()).isNull();
      assertThat(dto.getEmail()).isNull();
      assertThat(dto.getEmailSecondaire()).isNull();
      assertThat(dto.getWhatsapp()).isNull();
      assertThat(dto.getTelegram()).isNull();
      assertThat(dto.getAutresContacts()).isNull();
      assertThat(dto.getAdressePhysique()).isNull();
      assertThat(dto.getRencontresPhysiquesPossibles()).isNull();
      assertThat(dto.getAppelsAcceptes()).isNull();
      assertThat(dto.getSmsAcceptes()).isNull();
      assertThat(dto.getEmailsAcceptes()).isNull();
      assertThat(dto.getHorairesDisponibilite()).isNull();
      assertThat(dto.getLanguesPreferees()).isNull();
      assertThat(dto.getInstructionsSpeciales()).isNull();
    }

    @Test
    @DisplayName("Test collections vides")
    void testCollectionsVides() {
      ContactProposantDTO dto = new ContactProposantDTO();

      dto.setAutresContacts(new HashMap<>());
      dto.setLanguesPreferees(Arrays.asList());

      assertThat(dto.getAutresContacts()).isEmpty();
      assertThat(dto.getLanguesPreferees()).isEmpty();
    }
  }

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

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

    @Test
    @DisplayName("equals - objets égaux")
    void testEqualsObjetsEgaux() {
      ContactProposantDTO dto1 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();
      ContactProposantDTO dto2 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();

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

    @Test
    @DisplayName("equals - objets différents")
    void testEqualsObjetsDifferent() {
      ContactProposantDTO dto1 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();
      ContactProposantDTO dto2 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234568")
          .email("test@example.com")
          .build();

      assertThat(dto1).isNotEqualTo(dto2);
    }

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

    @Test
    @DisplayName("equals - objets avec tous les champs identiques")
    void testEqualsTousChampsIdentiques() {
      Map<String, String> autresContacts = new HashMap<>();
      autresContacts.put("facebook", "user123");
      List<String> langues = Arrays.asList("fr", "en");

      ContactProposantDTO dto1 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .telephoneSecondaire("+221771234568")
          .email("test@example.com")
          .emailSecondaire("test2@example.com")
          .whatsapp("+221771234569")
          .telegram("telegram123")
          .autresContacts(autresContacts)
          .adressePhysique("123 Rue Test")
          .rencontresPhysiquesPossibles(true)
          .appelsAcceptes(false)
          .smsAcceptes(true)
          .emailsAcceptes(false)
          .horairesDisponibilite("9h-17h")
          .languesPreferees(langues)
          .instructionsSpeciales("Instructions spéciales")
          .build();

      Map<String, String> autresContacts2 = new HashMap<>();
      autresContacts2.put("facebook", "user123");
      List<String> langues2 = Arrays.asList("fr", "en");

      ContactProposantDTO dto2 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .telephoneSecondaire("+221771234568")
          .email("test@example.com")
          .emailSecondaire("test2@example.com")
          .whatsapp("+221771234569")
          .telegram("telegram123")
          .autresContacts(autresContacts2)
          .adressePhysique("123 Rue Test")
          .rencontresPhysiquesPossibles(true)
          .appelsAcceptes(false)
          .smsAcceptes(true)
          .emailsAcceptes(false)
          .horairesDisponibilite("9h-17h")
          .languesPreferees(langues2)
          .instructionsSpeciales("Instructions spéciales")
          .build();

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

    @Test
    @DisplayName("hashCode - objets égaux ont même hashCode")
    void testHashCodeObjetsEgaux() {
      ContactProposantDTO dto1 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();
      ContactProposantDTO dto2 = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();

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

    @Test
    @DisplayName("toString - contient les informations essentielles")
    void testToString() {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("ContactProposantDTO");
      assertThat(toString).contains("+221771234567");
      assertThat(toString).contains("test@example.com");
    }

    @Test
    @DisplayName("toString - avec tous les champs")
    void testToStringTousChamps() {
      Map<String, String> autresContacts = new HashMap<>();
      autresContacts.put("facebook", "user123");
      List<String> langues = Arrays.asList("fr", "en");

      ContactProposantDTO dto = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .telephoneSecondaire("+221771234568")
          .email("test@example.com")
          .emailSecondaire("test2@example.com")
          .whatsapp("+221771234569")
          .telegram("telegram123")
          .autresContacts(autresContacts)
          .adressePhysique("123 Rue Test")
          .rencontresPhysiquesPossibles(true)
          .appelsAcceptes(false)
          .smsAcceptes(true)
          .emailsAcceptes(false)
          .horairesDisponibilite("9h-17h")
          .languesPreferees(langues)
          .instructionsSpeciales("Instructions spéciales")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("ContactProposantDTO");
      assertThat(toString).contains("+221771234567");
      assertThat(toString).contains("test@example.com");
    }
  }

  @Nested
  @DisplayName("Tests Builder complet - tous les champs")
  class BuilderCompletTests {

    @Test
    @DisplayName("Builder - tous les champs")
    void testBuilderTousChamps() {
      Map<String, String> autresContacts = new HashMap<>();
      autresContacts.put("facebook", "user123");
      autresContacts.put("linkedin", "user456");
      List<String> langues = Arrays.asList("fr", "en", "ar");

      ContactProposantDTO dto = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .telephoneSecondaire("+221771234568")
          .email("test@example.com")
          .emailSecondaire("test2@example.com")
          .whatsapp("+221771234569")
          .telegram("telegram123")
          .autresContacts(autresContacts)
          .adressePhysique("123 Rue Test")
          .rencontresPhysiquesPossibles(true)
          .appelsAcceptes(false)
          .smsAcceptes(true)
          .emailsAcceptes(false)
          .horairesDisponibilite("9h-17h")
          .languesPreferees(langues)
          .instructionsSpeciales("Instructions spéciales")
          .build();

      assertThat(dto.getTelephonePrincipal()).isEqualTo("+221771234567");
      assertThat(dto.getTelephoneSecondaire()).isEqualTo("+221771234568");
      assertThat(dto.getEmail()).isEqualTo("test@example.com");
      assertThat(dto.getEmailSecondaire()).isEqualTo("test2@example.com");
      assertThat(dto.getWhatsapp()).isEqualTo("+221771234569");
      assertThat(dto.getTelegram()).isEqualTo("telegram123");
      assertThat(dto.getAutresContacts()).isEqualTo(autresContacts);
      assertThat(dto.getAdressePhysique()).isEqualTo("123 Rue Test");
      assertThat(dto.getRencontresPhysiquesPossibles()).isTrue();
      assertThat(dto.getAppelsAcceptes()).isFalse();
      assertThat(dto.getSmsAcceptes()).isTrue();
      assertThat(dto.getEmailsAcceptes()).isFalse();
      assertThat(dto.getHorairesDisponibilite()).isEqualTo("9h-17h");
      assertThat(dto.getLanguesPreferees()).isEqualTo(langues);
      assertThat(dto.getInstructionsSpeciales()).isEqualTo("Instructions spéciales");
    }

    @Test
    @DisplayName("Builder - valeurs par défaut")
    void testBuilderValeursParDefaut() {
      ContactProposantDTO dto = ContactProposantDTO.builder()
          .telephonePrincipal("+221771234567")
          .email("test@example.com")
          .build();

      assertThat(dto.getRencontresPhysiquesPossibles()).isFalse();
      assertThat(dto.getAppelsAcceptes()).isTrue();
      assertThat(dto.getSmsAcceptes()).isTrue();
      assertThat(dto.getEmailsAcceptes()).isTrue();
    }
  }
}

