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

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

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 CritereSelectionDTO")
class CritereSelectionDTOTest {

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

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

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

      assertThat(dto).isNotNull();
      assertThat(dto.getEstObligatoire()).isFalse();
      assertThat(dto.getPoids()).isEqualTo(5);
    }

    @Test
    @DisplayName("Constructeur avec tous les paramètres")
    void testConstructeurAvecParametres() {
      CritereSelectionDTO dto = new CritereSelectionDTO(
          "Âge minimum",
          "age",
          "greater_than",
          "18",
          "65",
          true,
          8,
          "Critère d'âge entre 18 et 65 ans");

      assertThat(dto.getNom()).isEqualTo("Âge minimum");
      assertThat(dto.getType()).isEqualTo("age");
      assertThat(dto.getOperateur()).isEqualTo("greater_than");
      assertThat(dto.getValeur()).isEqualTo("18");
      assertThat(dto.getValeurMax()).isEqualTo("65");
      assertThat(dto.getEstObligatoire()).isTrue();
      assertThat(dto.getPoids()).isEqualTo(8);
      assertThat(dto.getDescription()).isEqualTo("Critère d'âge entre 18 et 65 ans");
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      CritereSelectionDTO dto = CritereSelectionDTO.builder()
          .nom("Situation familiale")
          .type("situation")
          .operateur("equals")
          .valeur("Famille nombreuse")
          .valeurMax(null)
          .estObligatoire(false)
          .poids(6)
          .description("Famille avec 3 enfants ou plus")
          .build();

      assertThat(dto.getNom()).isEqualTo("Situation familiale");
      assertThat(dto.getType()).isEqualTo("situation");
      assertThat(dto.getOperateur()).isEqualTo("equals");
      assertThat(dto.getValeur()).isEqualTo("Famille nombreuse");
      assertThat(dto.getValeurMax()).isNull();
      assertThat(dto.getEstObligatoire()).isFalse();
      assertThat(dto.getPoids()).isEqualTo(6);
      assertThat(dto.getDescription()).isEqualTo("Famille avec 3 enfants ou plus");
    }
  }

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

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("estObligatoire - toutes les valeurs")
    void testEstObligatoire(Boolean valeur) {
      CritereSelectionDTO dto = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .estObligatoire(valeur)
          .build();
      assertThat(dto.getEstObligatoire()).isEqualTo(valeur);
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "5, 5",
        "10, 10"
    })
    @DisplayName("poids - toutes les valeurs")
    void testPoids(Integer poids, Integer expected) {
      CritereSelectionDTO dto = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .poids(poids)
          .build();
      assertThat(dto.getPoids()).isEqualTo(expected);
    }
  }

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

    @Test
    @DisplayName("Test tous les champs avec setters directs")
    void testTousLesChampsSetters() {
      CritereSelectionDTO dto = new CritereSelectionDTO();

      dto.setNom("Âge minimum");
      dto.setType("age");
      dto.setOperateur("greater_than");
      dto.setValeur("18");
      dto.setValeurMax("65");
      dto.setEstObligatoire(true);
      dto.setPoids(8);
      dto.setDescription("Critère d'âge entre 18 et 65 ans");

      assertThat(dto.getNom()).isEqualTo("Âge minimum");
      assertThat(dto.getType()).isEqualTo("age");
      assertThat(dto.getOperateur()).isEqualTo("greater_than");
      assertThat(dto.getValeur()).isEqualTo("18");
      assertThat(dto.getValeurMax()).isEqualTo("65");
      assertThat(dto.getEstObligatoire()).isTrue();
      assertThat(dto.getPoids()).isEqualTo(8);
      assertThat(dto.getDescription()).isEqualTo("Critère d'âge entre 18 et 65 ans");
    }

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

      dto.setNom(null);
      dto.setType(null);
      dto.setOperateur(null);
      dto.setValeur(null);
      dto.setValeurMax(null);
      dto.setEstObligatoire(null);
      dto.setPoids(null);
      dto.setDescription(null);

      assertThat(dto.getNom()).isNull();
      assertThat(dto.getType()).isNull();
      assertThat(dto.getOperateur()).isNull();
      assertThat(dto.getValeur()).isNull();
      assertThat(dto.getValeurMax()).isNull();
      assertThat(dto.getEstObligatoire()).isNull();
      assertThat(dto.getPoids()).isNull();
      assertThat(dto.getDescription()).isNull();
    }
  }

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

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

    @Test
    @DisplayName("equals - objets égaux")
    void testEqualsObjetsEgaux() {
      CritereSelectionDTO dto1 = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .valeurMax("max")
          .estObligatoire(true)
          .poids(8)
          .description("Description")
          .build();
      CritereSelectionDTO dto2 = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .valeurMax("max")
          .estObligatoire(true)
          .poids(8)
          .description("Description")
          .build();

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

    @Test
    @DisplayName("equals - objets différents")
    void testEqualsObjetsDifferent() {
      CritereSelectionDTO dto1 = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .build();
      CritereSelectionDTO dto2 = CritereSelectionDTO.builder()
          .nom("Test2")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .build();

      assertThat(dto1).isNotEqualTo(dto2);
    }

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

    @Test
    @DisplayName("hashCode - objets égaux ont même hashCode")
    void testHashCodeObjetsEgaux() {
      CritereSelectionDTO dto1 = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .build();
      CritereSelectionDTO dto2 = CritereSelectionDTO.builder()
          .nom("Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .build();

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

    @Test
    @DisplayName("toString - contient les informations essentielles")
    void testToString() {
      CritereSelectionDTO dto = CritereSelectionDTO.builder()
          .nom("Critère Test")
          .type("test")
          .operateur("equals")
          .valeur("test")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("CritereSelectionDTO");
      assertThat(toString).contains("Critère Test");
      assertThat(toString).contains("test");
    }

    @Test
    @DisplayName("toString - avec tous les champs")
    void testToStringTousChamps() {
      CritereSelectionDTO dto = CritereSelectionDTO.builder()
          .nom("Âge minimum")
          .type("age")
          .operateur("greater_than")
          .valeur("18")
          .valeurMax("65")
          .estObligatoire(true)
          .poids(8)
          .description("Critère d'âge entre 18 et 65 ans")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("CritereSelectionDTO");
      assertThat(toString).contains("Âge minimum");
    }
  }
}

