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

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

import java.time.LocalDateTime;
import java.util.UUID;
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.EnumSource;
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;

/**
 * Tests unitaires pour DemandeAideResponse.
 * Couvre estModifiable, peutEtreAnnulee, estUrgente, estTerminee, estEnSucces,
 * getPourcentageAvancement (tous statuts), getDelaiRestantHeures, estDelaiDepasse,
 * getStatutLibelle, getPrioriteLibelle.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-21
 */
@DisplayName("Tests DemandeAideResponse")
class DemandeAideResponseTest {

  @Nested
  @DisplayName("estModifiable")
  class EstModifiable {

    @Test
    @DisplayName("retourne false quand statut null")
    void testStatutNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(null).build();
      assertThat(r.estModifiable()).isFalse();
    }

    @Test
    @DisplayName("retourne true pour BROUILLON")
    void testBrouillon() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.BROUILLON).build();
      assertThat(r.estModifiable()).isTrue();
    }

    @Test
    @DisplayName("retourne true pour INFORMATIONS_REQUISES")
    void testInformationsRequises() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.INFORMATIONS_REQUISES).build();
      assertThat(r.estModifiable()).isTrue();
    }

    @Test
    @DisplayName("retourne false pour SOUMISE")
    void testSoumise() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.SOUMISE).build();
      assertThat(r.estModifiable()).isFalse();
    }
  }

  @Nested
  @DisplayName("peutEtreAnnulee")
  class PeutEtreAnnulee {

    @Test
    @DisplayName("retourne false quand statut null")
    void testStatutNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(null).build();
      assertThat(r.peutEtreAnnulee()).isFalse();
    }

    @Test
    @DisplayName("retourne true pour BROUILLON")
    void testBrouillon() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.BROUILLON).build();
      assertThat(r.peutEtreAnnulee()).isTrue();
    }

    @Test
    @DisplayName("retourne false pour VERSEE (final)")
    void testFinal() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.VERSEE).build();
      assertThat(r.peutEtreAnnulee()).isFalse();
    }

    @Test
    @DisplayName("retourne false pour ANNULEE")
    void testAnnulee() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.ANNULEE).build();
      assertThat(r.peutEtreAnnulee()).isFalse();
    }
  }

  @Nested
  @DisplayName("estUrgente")
  class EstUrgente {

    @Test
    @DisplayName("retourne false quand priorite null")
    void testPrioriteNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().priorite(null).build();
      assertThat(r.estUrgente()).isFalse();
    }

    @Test
    @DisplayName("retourne true pour CRITIQUE")
    void testCritique() {
      DemandeAideResponse r = DemandeAideResponse.builder().priorite(PrioriteAide.CRITIQUE).build();
      assertThat(r.estUrgente()).isTrue();
    }

    @Test
    @DisplayName("retourne true pour URGENTE")
    void testUrgente() {
      DemandeAideResponse r = DemandeAideResponse.builder().priorite(PrioriteAide.URGENTE).build();
      assertThat(r.estUrgente()).isTrue();
    }

    @Test
    @DisplayName("retourne false pour NORMALE")
    void testNormale() {
      DemandeAideResponse r = DemandeAideResponse.builder().priorite(PrioriteAide.NORMALE).build();
      assertThat(r.estUrgente()).isFalse();
    }
  }

  @Nested
  @DisplayName("estTerminee")
  class EstTerminee {

    @Test
    @DisplayName("retourne false quand statut null")
    void testStatutNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(null).build();
      assertThat(r.estTerminee()).isFalse();
    }

    @Test
    @DisplayName("retourne true pour VERSEE")
    void testTerminee() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.VERSEE).build();
      assertThat(r.estTerminee()).isTrue();
    }

    @Test
    @DisplayName("retourne false pour EN_ATTENTE")
    void testEnCours() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.EN_ATTENTE).build();
      assertThat(r.estTerminee()).isFalse();
    }
  }

  @Nested
  @DisplayName("estEnSucces")
  class EstEnSucces {

    @Test
    @DisplayName("retourne false quand statut null")
    void testStatutNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(null).build();
      assertThat(r.estEnSucces()).isFalse();
    }

    @Test
    @DisplayName("retourne true pour VERSEE")
    void testVersee() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.VERSEE).build();
      assertThat(r.estEnSucces()).isTrue();
    }

    @Test
    @DisplayName("retourne false pour REJETEE")
    void testRejetee() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.REJETEE).build();
      assertThat(r.estEnSucces()).isFalse();
    }
  }

  @Nested
  @DisplayName("getPourcentageAvancement")
  class GetPourcentageAvancement {

    @Test
    @DisplayName("retourne 0 quand statut null")
    void testStatutNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(null).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(0.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"BROUILLON"})
    @DisplayName("BROUILLON -> 5.0")
    void testBrouillon(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(5.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"SOUMISE"})
    @DisplayName("SOUMISE -> 10.0")
    void testSoumise(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(10.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"EN_ATTENTE"})
    @DisplayName("EN_ATTENTE -> 20.0")
    void testEnAttente(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(20.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"EN_COURS_EVALUATION"})
    @DisplayName("EN_COURS_EVALUATION -> 40.0")
    void testEnCoursEvaluation(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(40.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"INFORMATIONS_REQUISES"})
    @DisplayName("INFORMATIONS_REQUISES -> 35.0")
    void testInformationsRequises(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(35.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"APPROUVEE", "APPROUVEE_PARTIELLEMENT"})
    @DisplayName("APPROUVEE/APPROUVEE_PARTIELLEMENT -> 60.0")
    void testApprouvee(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(60.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"EN_COURS_TRAITEMENT"})
    @DisplayName("EN_COURS_TRAITEMENT -> 70.0")
    void testEnCoursTraitement(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(70.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"EN_COURS_VERSEMENT"})
    @DisplayName("EN_COURS_VERSEMENT -> 85.0")
    void testEnCoursVersement(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(85.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"VERSEE", "LIVREE", "TERMINEE"})
    @DisplayName("VERSEE/LIVREE/TERMINEE -> 100.0")
    void testFinauxSucces(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(100.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"REJETEE", "ANNULEE", "EXPIREE"})
    @DisplayName("REJETEE/ANNULEE/EXPIREE -> 100.0")
    void testFinauxEchec(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(100.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"SUSPENDUE"})
    @DisplayName("SUSPENDUE -> 50.0")
    void testSuspendue(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(50.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"EN_SUIVI"})
    @DisplayName("EN_SUIVI -> 95.0")
    void testEnSuivi(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(95.0);
    }

    @ParameterizedTest
    @EnumSource(value = StatutAide.class, names = {"CLOTUREE"})
    @DisplayName("CLOTUREE -> 100.0")
    void testCloturee(StatutAide statut) {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(statut).build();
      assertThat(r.getPourcentageAvancement()).isEqualTo(100.0);
    }
  }

  @Nested
  @DisplayName("getDelaiRestantHeures")
  class GetDelaiRestantHeures {

    @Test
    @DisplayName("retourne -1 quand dateLimiteTraitement null")
    void testNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().dateLimiteTraitement(null).build();
      assertThat(r.getDelaiRestantHeures()).isEqualTo(-1);
    }

    @Test
    @DisplayName("retourne 0 quand date limite dépassée")
    void testDepasse() {
      LocalDateTime hier = LocalDateTime.now().minusHours(1);
      DemandeAideResponse r = DemandeAideResponse.builder().dateLimiteTraitement(hier).build();
      assertThat(r.getDelaiRestantHeures()).isEqualTo(0);
    }

    @Test
    @DisplayName("retourne heures restantes quand date limite future")
    void testRestant() {
      LocalDateTime demain = LocalDateTime.now().plusHours(24);
      DemandeAideResponse r = DemandeAideResponse.builder().dateLimiteTraitement(demain).build();
      assertThat(r.getDelaiRestantHeures()).isGreaterThanOrEqualTo(23);
    }
  }

  @Nested
  @DisplayName("estDelaiDepasse")
  class EstDelaiDepasse {

    @Test
    @DisplayName("retourne true quand delai restant 0")
    void testDepasse() {
      LocalDateTime hier = LocalDateTime.now().minusHours(1);
      DemandeAideResponse r = DemandeAideResponse.builder().dateLimiteTraitement(hier).build();
      assertThat(r.estDelaiDepasse()).isTrue();
    }

    @Test
    @DisplayName("retourne false quand delai restant > 0")
    void testNonDepasse() {
      LocalDateTime demain = LocalDateTime.now().plusHours(24);
      DemandeAideResponse r = DemandeAideResponse.builder().dateLimiteTraitement(demain).build();
      assertThat(r.estDelaiDepasse()).isFalse();
    }
  }

  @Nested
  @DisplayName("getStatutLibelle")
  class GetStatutLibelle {

    @Test
    @DisplayName("retourne Non défini quand statut null")
    void testNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(null).build();
      assertThat(r.getStatutLibelle()).isEqualTo("Non défini");
    }

    @Test
    @DisplayName("retourne libellé du statut")
    void testLibelle() {
      DemandeAideResponse r = DemandeAideResponse.builder().statut(StatutAide.BROUILLON).build();
      assertThat(r.getStatutLibelle()).isEqualTo("Brouillon");
    }
  }

  @Nested
  @DisplayName("getPrioriteLibelle")
  class GetPrioriteLibelle {

    @Test
    @DisplayName("retourne Normale quand priorite null")
    void testNull() {
      DemandeAideResponse r = DemandeAideResponse.builder().priorite(null).build();
      assertThat(r.getPrioriteLibelle()).isEqualTo("Normale");
    }

    @Test
    @DisplayName("retourne libellé de la priorité")
    void testLibelle() {
      DemandeAideResponse r = DemandeAideResponse.builder().priorite(PrioriteAide.URGENTE).build();
      assertThat(r.getPrioriteLibelle()).isEqualTo("Urgente");
    }
  }

  @Nested
  @DisplayName("Builder et getters")
  class BuilderEtGetters {

    @Test
    @DisplayName("builder remplit les champs")
    void testBuilder() {
      UUID id = UUID.randomUUID();
      DemandeAideResponse r = DemandeAideResponse.builder()
          .numeroReference("REF-001")
          .titre("Titre")
          .statut(StatutAide.SOUMISE)
          .priorite(PrioriteAide.NORMALE)
          .build();
      r.setId(id);
      assertThat(r.getId()).isEqualTo(id);
      assertThat(r.getNumeroReference()).isEqualTo("REF-001");
      assertThat(r.getTitre()).isEqualTo("Titre");
      assertThat(r.getStatut()).isEqualTo(StatutAide.SOUMISE);
      assertThat(r.getPriorite()).isEqualTo(PrioriteAide.NORMALE);
    }
  }
}
