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

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

import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import java.time.LocalDateTime;
import java.util.HashMap;
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 HistoriqueStatutDTO")
class HistoriqueStatutDTOTest {

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

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

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

      assertThat(dto).isNotNull();
      assertThat(dto.getDateChangement()).isNotNull();
      assertThat(dto.getEstAutomatique()).isFalse();
    }

    @Test
    @DisplayName("Constructeur avec tous les paramètres")
    void testConstructeurAvecParametres() {
      LocalDateTime dateChangement = LocalDateTime.of(2025, 1, 15, 10, 0);
      Map<String, Object> donnees = new HashMap<>();
      donnees.put("key", "value");

      HistoriqueStatutDTO dto = new HistoriqueStatutDTO(
          "id-123",
          StatutAide.BROUILLON,
          StatutAide.EN_ATTENTE,
          dateChangement,
          "auteur-id",
          "Auteur Nom",
          "Changement de statut",
          "Commentaires additionnels",
          false,
          120L,
          donnees);

      assertThat(dto.getId()).isEqualTo("id-123");
      assertThat(dto.getAncienStatut()).isEqualTo(StatutAide.BROUILLON);
      assertThat(dto.getNouveauStatut()).isEqualTo(StatutAide.EN_ATTENTE);
      assertThat(dto.getDateChangement()).isEqualTo(dateChangement);
      assertThat(dto.getAuteurId()).isEqualTo("auteur-id");
      assertThat(dto.getAuteurNom()).isEqualTo("Auteur Nom");
      assertThat(dto.getMotif()).isEqualTo("Changement de statut");
      assertThat(dto.getCommentaires()).isEqualTo("Commentaires additionnels");
      assertThat(dto.getEstAutomatique()).isFalse();
      assertThat(dto.getDureeDepuisPrecedent()).isEqualTo(120L);
      assertThat(dto.getDonneesAdditionnelles()).isEqualTo(donnees);
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      LocalDateTime dateChangement = LocalDateTime.of(2025, 1, 15, 11, 0);
      HistoriqueStatutDTO dto = HistoriqueStatutDTO.builder()
          .id("id-456")
          .ancienStatut(StatutAide.EN_ATTENTE)
          .nouveauStatut(StatutAide.APPROUVEE)
          .dateChangement(dateChangement)
          .auteurId("auteur-456")
          .auteurNom("Test Auteur")
          .motif("Approbation de la demande")
          .commentaires("Demande approuvée après évaluation")
          .estAutomatique(false)
          .dureeDepuisPrecedent(180L)
          .build();

      assertThat(dto.getId()).isEqualTo("id-456");
      assertThat(dto.getAncienStatut()).isEqualTo(StatutAide.EN_ATTENTE);
      assertThat(dto.getNouveauStatut()).isEqualTo(StatutAide.APPROUVEE);
      assertThat(dto.getDateChangement()).isEqualTo(dateChangement);
      assertThat(dto.getAuteurId()).isEqualTo("auteur-456");
      assertThat(dto.getAuteurNom()).isEqualTo("Test Auteur");
      assertThat(dto.getMotif()).isEqualTo("Approbation de la demande");
      assertThat(dto.getCommentaires()).isEqualTo("Demande approuvée après évaluation");
      assertThat(dto.getEstAutomatique()).isFalse();
      assertThat(dto.getDureeDepuisPrecedent()).isEqualTo(180L);
    }
  }

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

    @Test
    @DisplayName("dateChangement - initialisée par défaut")
    void testDateChangementParDefaut() {
      HistoriqueStatutDTO dto = HistoriqueStatutDTO.builder()
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .auteurId("auteur")
          .build();

      assertThat(dto.getDateChangement()).isNotNull();
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    @DisplayName("estAutomatique - toutes les valeurs")
    void testEstAutomatique(Boolean valeur) {
      HistoriqueStatutDTO dto = HistoriqueStatutDTO.builder()
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .auteurId("auteur")
          .estAutomatique(valeur)
          .build();
      assertThat(dto.getEstAutomatique()).isEqualTo(valeur);
    }
  }

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

    @Test
    @DisplayName("Test tous les champs avec setters directs")
    void testTousLesChampsSetters() {
      HistoriqueStatutDTO dto = new HistoriqueStatutDTO();
      LocalDateTime dateChangement = LocalDateTime.of(2025, 1, 15, 10, 0);
      Map<String, Object> donnees = new HashMap<>();
      donnees.put("key1", "value1");
      donnees.put("key2", 123);

      dto.setId("id-123");
      dto.setAncienStatut(StatutAide.BROUILLON);
      dto.setNouveauStatut(StatutAide.EN_ATTENTE);
      dto.setDateChangement(dateChangement);
      dto.setAuteurId("auteur-id");
      dto.setAuteurNom("Auteur Nom");
      dto.setMotif("Changement de statut");
      dto.setCommentaires("Commentaires additionnels");
      dto.setEstAutomatique(true);
      dto.setDureeDepuisPrecedent(120L);
      dto.setDonneesAdditionnelles(donnees);

      assertThat(dto.getId()).isEqualTo("id-123");
      assertThat(dto.getAncienStatut()).isEqualTo(StatutAide.BROUILLON);
      assertThat(dto.getNouveauStatut()).isEqualTo(StatutAide.EN_ATTENTE);
      assertThat(dto.getDateChangement()).isEqualTo(dateChangement);
      assertThat(dto.getAuteurId()).isEqualTo("auteur-id");
      assertThat(dto.getAuteurNom()).isEqualTo("Auteur Nom");
      assertThat(dto.getMotif()).isEqualTo("Changement de statut");
      assertThat(dto.getCommentaires()).isEqualTo("Commentaires additionnels");
      assertThat(dto.getEstAutomatique()).isTrue();
      assertThat(dto.getDureeDepuisPrecedent()).isEqualTo(120L);
      assertThat(dto.getDonneesAdditionnelles()).isEqualTo(donnees);
    }

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

      dto.setId(null);
      dto.setAncienStatut(null);
      dto.setNouveauStatut(null);
      dto.setDateChangement(null);
      dto.setAuteurId(null);
      dto.setAuteurNom(null);
      dto.setMotif(null);
      dto.setCommentaires(null);
      dto.setEstAutomatique(null);
      dto.setDureeDepuisPrecedent(null);
      dto.setDonneesAdditionnelles(null);

      assertThat(dto.getId()).isNull();
      assertThat(dto.getAncienStatut()).isNull();
      assertThat(dto.getNouveauStatut()).isNull();
      assertThat(dto.getDateChangement()).isNull();
      assertThat(dto.getAuteurId()).isNull();
      assertThat(dto.getAuteurNom()).isNull();
      assertThat(dto.getMotif()).isNull();
      assertThat(dto.getCommentaires()).isNull();
      assertThat(dto.getEstAutomatique()).isNull();
      assertThat(dto.getDureeDepuisPrecedent()).isNull();
      assertThat(dto.getDonneesAdditionnelles()).isNull();
    }
  }

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

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

    @Test
    @DisplayName("equals - objets égaux")
    void testEqualsObjetsEgaux() {
      LocalDateTime dateChangement = LocalDateTime.of(2025, 1, 15, 10, 0);
      HistoriqueStatutDTO dto1 = HistoriqueStatutDTO.builder()
          .id("id-1")
          .ancienStatut(StatutAide.BROUILLON)
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .dateChangement(dateChangement)
          .auteurId("auteur")
          .auteurNom("Auteur Nom")
          .motif("Motif")
          .commentaires("Commentaires")
          .estAutomatique(false)
          .dureeDepuisPrecedent(120L)
          .build();
      HistoriqueStatutDTO dto2 = HistoriqueStatutDTO.builder()
          .id("id-1")
          .ancienStatut(StatutAide.BROUILLON)
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .dateChangement(dateChangement)
          .auteurId("auteur")
          .auteurNom("Auteur Nom")
          .motif("Motif")
          .commentaires("Commentaires")
          .estAutomatique(false)
          .dureeDepuisPrecedent(120L)
          .build();

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

    @Test
    @DisplayName("equals - objets différents")
    void testEqualsObjetsDifferent() {
      HistoriqueStatutDTO dto1 = HistoriqueStatutDTO.builder()
          .id("id-1")
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .auteurId("auteur")
          .build();
      HistoriqueStatutDTO dto2 = HistoriqueStatutDTO.builder()
          .id("id-2")
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .auteurId("auteur")
          .build();

      assertThat(dto1).isNotEqualTo(dto2);
    }

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

    @Test
    @DisplayName("hashCode - objets égaux ont même hashCode")
    void testHashCodeObjetsEgaux() {
      LocalDateTime dateChangement = LocalDateTime.of(2025, 1, 15, 10, 0);
      HistoriqueStatutDTO dto1 = HistoriqueStatutDTO.builder()
          .id("id-1")
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .dateChangement(dateChangement)
          .auteurId("auteur")
          .build();
      HistoriqueStatutDTO dto2 = HistoriqueStatutDTO.builder()
          .id("id-1")
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .dateChangement(dateChangement)
          .auteurId("auteur")
          .build();

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

    @Test
    @DisplayName("toString - contient les informations essentielles")
    void testToString() {
      HistoriqueStatutDTO dto = HistoriqueStatutDTO.builder()
          .id("id-123")
          .ancienStatut(StatutAide.BROUILLON)
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .auteurNom("Auteur")
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("HistoriqueStatutDTO");
      assertThat(toString).contains("id-123");
      assertThat(toString).contains("Auteur");
    }

    @Test
    @DisplayName("toString - avec tous les champs")
    void testToStringTousChamps() {
      LocalDateTime dateChangement = LocalDateTime.of(2025, 1, 15, 10, 0);
      Map<String, Object> donnees = new HashMap<>();
      donnees.put("key", "value");

      HistoriqueStatutDTO dto = HistoriqueStatutDTO.builder()
          .id("id-123")
          .ancienStatut(StatutAide.BROUILLON)
          .nouveauStatut(StatutAide.EN_ATTENTE)
          .dateChangement(dateChangement)
          .auteurId("auteur-id")
          .auteurNom("Auteur Nom")
          .motif("Motif")
          .commentaires("Commentaires")
          .estAutomatique(true)
          .dureeDepuisPrecedent(120L)
          .donneesAdditionnelles(donnees)
          .build();

      String toString = dto.toString();
      assertThat(toString).contains("HistoriqueStatutDTO");
      assertThat(toString).contains("id-123");
      assertThat(toString).contains("Auteur Nom");
    }
  }
}

