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 LocalisationDTO")
class LocalisationDTOTest {

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

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

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

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

    @Test
    @DisplayName("Constructeur avec tous les paramètres")
    void testConstructeurAvecParametres() {
      LocalisationDTO dto = new LocalisationDTO(
          14.6937,
          -17.4441,
          "123 Rue Test, Dakar, Sénégal",
          "Dakar",
          "Dakar",
          "Sénégal",
          "12345",
          10.5,
          true);

      assertThat(dto.getLatitude()).isEqualTo(14.6937);
      assertThat(dto.getLongitude()).isEqualTo(-17.4441);
      assertThat(dto.getAdresseComplete()).isEqualTo("123 Rue Test, Dakar, Sénégal");
      assertThat(dto.getVille()).isEqualTo("Dakar");
      assertThat(dto.getRegion()).isEqualTo("Dakar");
      assertThat(dto.getPays()).isEqualTo("Sénégal");
      assertThat(dto.getCodePostal()).isEqualTo("12345");
      assertThat(dto.getPrecision()).isEqualTo(10.5);
      assertThat(dto.getEstApproximative()).isTrue();
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      LocalisationDTO dto = LocalisationDTO.builder()
          .latitude(14.7167)
          .longitude(-17.4677)
          .adresseComplete("456 Rue Autre, Thiès, Sénégal")
          .ville("Thiès")
          .region("Thiès")
          .pays("Sénégal")
          .codePostal("54321")
          .precision(5.0)
          .estApproximative(false)
          .build();

      assertThat(dto.getLatitude()).isEqualTo(14.7167);
      assertThat(dto.getLongitude()).isEqualTo(-17.4677);
      assertThat(dto.getAdresseComplete()).isEqualTo("456 Rue Autre, Thiès, Sénégal");
      assertThat(dto.getVille()).isEqualTo("Thiès");
      assertThat(dto.getRegion()).isEqualTo("Thiès");
      assertThat(dto.getPays()).isEqualTo("Sénégal");
      assertThat(dto.getCodePostal()).isEqualTo("54321");
      assertThat(dto.getPrecision()).isEqualTo(5.0);
      assertThat(dto.getEstApproximative()).isFalse();
    }
  }

  @Nested
  @DisplayName("Tests valeurs limites")
  class ValeursLimitesTests {

    @ParameterizedTest
    @CsvSource({
        "-90.0, -90.0",
        "0.0, 0.0",
        "90.0, 90.0"
    })
    @DisplayName("latitude - valeurs limites")
    void testLatitudeLimites(Double latitude, Double expected) {
      LocalisationDTO dto = LocalisationDTO.builder()
          .latitude(latitude)
          .build();
      assertThat(dto.getLatitude()).isEqualTo(expected);
    }

    @ParameterizedTest
    @CsvSource({
        "-180.0, -180.0",
        "0.0, 0.0",
        "180.0, 180.0"
    })
    @DisplayName("longitude - valeurs limites")
    void testLongitudeLimites(Double longitude, Double expected) {
      LocalisationDTO dto = LocalisationDTO.builder()
          .longitude(longitude)
          .build();
      assertThat(dto.getLongitude()).isEqualTo(expected);
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("estApproximative - toutes les valeurs")
    void testEstApproximative(Boolean valeur) {
      LocalisationDTO dto = LocalisationDTO.builder()
          .estApproximative(valeur)
          .build();
      assertThat(dto.getEstApproximative()).isEqualTo(valeur);
    }
  }

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

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

      dto.setLatitude(14.6937);
      dto.setLongitude(-17.4441);
      dto.setAdresseComplete("123 Rue Test, Dakar, Sénégal");
      dto.setVille("Dakar");
      dto.setRegion("Dakar");
      dto.setPays("Sénégal");
      dto.setCodePostal("12345");
      dto.setPrecision(10.5);
      dto.setEstApproximative(true);

      assertThat(dto.getLatitude()).isEqualTo(14.6937);
      assertThat(dto.getLongitude()).isEqualTo(-17.4441);
      assertThat(dto.getAdresseComplete()).isEqualTo("123 Rue Test, Dakar, Sénégal");
      assertThat(dto.getVille()).isEqualTo("Dakar");
      assertThat(dto.getRegion()).isEqualTo("Dakar");
      assertThat(dto.getPays()).isEqualTo("Sénégal");
      assertThat(dto.getCodePostal()).isEqualTo("12345");
      assertThat(dto.getPrecision()).isEqualTo(10.5);
      assertThat(dto.getEstApproximative()).isTrue();
    }

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

      dto.setLatitude(null);
      dto.setLongitude(null);
      dto.setAdresseComplete(null);
      dto.setVille(null);
      dto.setRegion(null);
      dto.setPays(null);
      dto.setCodePostal(null);
      dto.setPrecision(null);
      dto.setEstApproximative(null);

      assertThat(dto.getLatitude()).isNull();
      assertThat(dto.getLongitude()).isNull();
      assertThat(dto.getAdresseComplete()).isNull();
      assertThat(dto.getVille()).isNull();
      assertThat(dto.getRegion()).isNull();
      assertThat(dto.getPays()).isNull();
      assertThat(dto.getCodePostal()).isNull();
      assertThat(dto.getPrecision()).isNull();
      assertThat(dto.getEstApproximative()).isNull();
    }
  }

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

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

    @Test
    @DisplayName("equals - objets égaux")
    void testEqualsObjetsEgaux() {
      LocalisationDTO dto1 = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .adresseComplete("123 Rue Test")
          .ville("Dakar")
          .region("Dakar")
          .pays("Sénégal")
          .codePostal("12345")
          .precision(10.5)
          .estApproximative(true)
          .build();
      LocalisationDTO dto2 = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .adresseComplete("123 Rue Test")
          .ville("Dakar")
          .region("Dakar")
          .pays("Sénégal")
          .codePostal("12345")
          .precision(10.5)
          .estApproximative(true)
          .build();

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

    @Test
    @DisplayName("equals - objets différents")
    void testEqualsObjetsDifferent() {
      LocalisationDTO dto1 = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .ville("Dakar")
          .build();
      LocalisationDTO dto2 = LocalisationDTO.builder()
          .latitude(14.6938)
          .longitude(-17.4441)
          .ville("Dakar")
          .build();

      assertThat(dto1).isNotEqualTo(dto2);
    }

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

    @Test
    @DisplayName("hashCode - objets égaux ont même hashCode")
    void testHashCodeObjetsEgaux() {
      LocalisationDTO dto1 = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .ville("Dakar")
          .build();
      LocalisationDTO dto2 = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .ville("Dakar")
          .build();

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

    @Test
    @DisplayName("toString - contient les informations essentielles")
    void testToString() {
      LocalisationDTO dto = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .ville("Dakar")
          .pays("Sénégal")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("LocalisationDTO");
      assertThat(toString).contains("14.6937");
      assertThat(toString).contains("-17.4441");
      assertThat(toString).contains("Dakar");
    }

    @Test
    @DisplayName("toString - avec tous les champs")
    void testToStringTousChamps() {
      LocalisationDTO dto = LocalisationDTO.builder()
          .latitude(14.6937)
          .longitude(-17.4441)
          .adresseComplete("123 Rue Test, Dakar, Sénégal")
          .ville("Dakar")
          .region("Dakar")
          .pays("Sénégal")
          .codePostal("12345")
          .precision(10.5)
          .estApproximative(true)
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("LocalisationDTO");
      assertThat(toString).contains("14.6937");
      assertThat(toString).contains("-17.4441");
      assertThat(toString).contains("Dakar");
    }
  }
}

