package dev.lions.unionflow.server.api.enums.notification;

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.EnumSource;

@DisplayName("Tests pour StatutNotification")
class StatutNotificationTest {

  @Nested
  @DisplayName("Tests des valeurs enum")
  class TestsValeursEnum {

    @Test
    @DisplayName("Test toutes les valeurs enum")
    void testToutesValeurs() {
      StatutNotification[] values = StatutNotification.values();
      assertThat(values).hasSize(23);
    }

    @ParameterizedTest
    @EnumSource(StatutNotification.class)
    @DisplayName("Test getters de base pour toutes les valeurs")
    void testGettersBase(StatutNotification statut) {
      assertThat(statut.getLibelle()).isNotNull().isNotEmpty();
      assertThat(statut.getCode()).isNotNull().isNotEmpty();
      assertThat(statut.getDescription()).isNotNull().isNotEmpty();
      assertThat(statut.getIcone()).isNotNull().isNotEmpty();
      assertThat(statut.getCouleur()).isNotNull().matches("#[0-9A-Fa-f]{6}");
    }

    @ParameterizedTest
    @CsvSource({
        "ENVOYEE, true",
        "RECUE, true",
        "AFFICHEE, true",
        "OUVERTE, true",
        "LUE, true",
        "ACTION_EXECUTEE, true",
        "BROUILLON, false",
        "PROGRAMMEE, false",
        "EN_ATTENTE, false"
    })
    @DisplayName("isVisibleUtilisateur - statuts visibles")
    void testIsVisibleUtilisateur(StatutNotification statut, Boolean expected) {
      assertThat(statut.isVisibleUtilisateur()).isEqualTo(expected);
    }

    @ParameterizedTest
    @CsvSource({
        "ECHEC_ENVOI, true",
        "PARTIELLEMENT_ENVOYEE, true",
        "ANNULEE, true",
        "ERREUR_TECHNIQUE, true",
        "DESTINATAIRE_INVALIDE, true",
        "TOKEN_INVALIDE, true",
        "QUOTA_DEPASSE, true",
        "ENVOYEE, false",
        "RECUE, false",
        "BROUILLON, false"
    })
    @DisplayName("isNecessiteAttention - statuts nécessitant attention")
    void testIsNecessiteAttention(StatutNotification statut, Boolean expected) {
      assertThat(statut.isNecessiteAttention()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isSucces")
  class IsSuccesTests {

    @ParameterizedTest
    @CsvSource({
        "ENVOYEE, true",
        "RECUE, true",
        "AFFICHEE, true",
        "OUVERTE, true",
        "LUE, true",
        "ACTION_EXECUTEE, true",
        "BROUILLON, false",
        "ECHEC_ENVOI, false"
    })
    @DisplayName("isSucces - statuts de succès")
    void testIsSucces(StatutNotification statut, Boolean expected) {
      assertThat(statut.isSucces()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isErreur")
  class IsErreurTests {

    @ParameterizedTest
    @CsvSource({
        "ECHEC_ENVOI, true",
        "ERREUR_TECHNIQUE, true",
        "DESTINATAIRE_INVALIDE, true",
        "TOKEN_INVALIDE, true",
        "QUOTA_DEPASSE, true",
        "ENVOYEE, false",
        "RECUE, false"
    })
    @DisplayName("isErreur - statuts d'erreur")
    void testIsErreur(StatutNotification statut, Boolean expected) {
      assertThat(statut.isErreur()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isEnCours")
  class IsEnCoursTests {

    @ParameterizedTest
    @CsvSource({
        "PROGRAMMEE, true",
        "EN_ATTENTE, true",
        "EN_COURS_ENVOI, true",
        "ENVOYEE, false",
        "RECUE, false"
    })
    @DisplayName("isEnCours - statuts en cours")
    void testIsEnCours(StatutNotification statut, Boolean expected) {
      assertThat(statut.isEnCours()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isFinal")
  class IsFinalTests {

    @ParameterizedTest
    @CsvSource({
        "SUPPRIMEE, true",
        "ARCHIVEE, true",
        "EXPIREE, true",
        "ANNULEE, true",
        "ERREUR_TECHNIQUE, true",
        "ENVOYEE, false",
        "RECUE, false"
    })
    @DisplayName("isFinal - statuts finaux")
    void testIsFinal(StatutNotification statut, Boolean expected) {
      assertThat(statut.isFinal()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests permetModification")
  class PermetModificationTests {

    @ParameterizedTest
    @CsvSource({
        "BROUILLON, true",
        "PROGRAMMEE, true",
        "EN_ATTENTE, false",
        "ENVOYEE, false"
    })
    @DisplayName("permetModification - statuts modifiables")
    void testPermetModification(StatutNotification statut, Boolean expected) {
      assertThat(statut.permetModification()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests permetAnnulation")
  class PermetAnnulationTests {

    @ParameterizedTest
    @CsvSource({
        "PROGRAMMEE, true",
        "EN_ATTENTE, true",
        "BROUILLON, false",
        "ENVOYEE, false"
    })
    @DisplayName("permetAnnulation - statuts annulables")
    void testPermetAnnulation(StatutNotification statut, Boolean expected) {
      assertThat(statut.permetAnnulation()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests getPrioriteAffichage")
  class GetPrioriteAffichageTests {

    @Test
    @DisplayName("getPrioriteAffichage - erreur = priorité 1")
    void testGetPrioriteAffichageErreur() {
      assertThat(StatutNotification.ERREUR_TECHNIQUE.getPrioriteAffichage()).isEqualTo(1);
      assertThat(StatutNotification.ECHEC_ENVOI.getPrioriteAffichage()).isEqualTo(1);
      assertThat(StatutNotification.DESTINATAIRE_INVALIDE.getPrioriteAffichage()).isEqualTo(1);
    }

    @Test
    @DisplayName("getPrioriteAffichage - nécessite attention = priorité 2")
    void testGetPrioriteAffichageNecessiteAttention() {
      // PARTIELLEMENT_ENVOYEE nécessite attention mais n'est pas une erreur
      assertThat(StatutNotification.PARTIELLEMENT_ENVOYEE.getPrioriteAffichage()).isEqualTo(2);
      assertThat(StatutNotification.ANNULEE.getPrioriteAffichage()).isEqualTo(2);
    }

    @Test
    @DisplayName("getPrioriteAffichage - en cours = priorité 3")
    void testGetPrioriteAffichageEnCours() {
      assertThat(StatutNotification.EN_COURS_ENVOI.getPrioriteAffichage()).isEqualTo(3);
      assertThat(StatutNotification.PROGRAMMEE.getPrioriteAffichage()).isEqualTo(3);
      assertThat(StatutNotification.EN_ATTENTE.getPrioriteAffichage()).isEqualTo(3);
    }

    @Test
    @DisplayName("getPrioriteAffichage - succès = priorité 4")
    void testGetPrioriteAffichageSucces() {
      assertThat(StatutNotification.ENVOYEE.getPrioriteAffichage()).isEqualTo(4);
      assertThat(StatutNotification.RECUE.getPrioriteAffichage()).isEqualTo(4);
      assertThat(StatutNotification.AFFICHEE.getPrioriteAffichage()).isEqualTo(4);
      assertThat(StatutNotification.OUVERTE.getPrioriteAffichage()).isEqualTo(4);
      assertThat(StatutNotification.LUE.getPrioriteAffichage()).isEqualTo(4);
      assertThat(StatutNotification.ACTION_EXECUTEE.getPrioriteAffichage()).isEqualTo(4);
    }

    @Test
    @DisplayName("getPrioriteAffichage - autres statuts = priorité 5")
    void testGetPrioriteAffichageAutres() {
      assertThat(StatutNotification.BROUILLON.getPrioriteAffichage()).isEqualTo(5);
      assertThat(StatutNotification.SUPPRIMEE.getPrioriteAffichage()).isEqualTo(5);
      assertThat(StatutNotification.ARCHIVEE.getPrioriteAffichage()).isEqualTo(5);
      assertThat(StatutNotification.EXPIREE.getPrioriteAffichage()).isEqualTo(5);
      assertThat(StatutNotification.IGNOREE.getPrioriteAffichage()).isEqualTo(5);
      assertThat(StatutNotification.NON_LUE.getPrioriteAffichage()).isEqualTo(5);
      assertThat(StatutNotification.MARQUEE_IMPORTANTE.getPrioriteAffichage()).isEqualTo(5);
    }
  }

  @Nested
  @DisplayName("Tests getStatutsSuivantsPossibles")
  class GetStatutsSuivantsPossiblesTests {

    @Test
    @DisplayName("getStatutsSuivantsPossibles - BROUILLON")
    void testGetStatutsSuivantsPossiblesBrouillon() {
      StatutNotification[] suivants = StatutNotification.BROUILLON.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.PROGRAMMEE,
          StatutNotification.EN_ATTENTE,
          StatutNotification.ANNULEE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - PROGRAMMEE")
    void testGetStatutsSuivantsPossiblesProgrammee() {
      StatutNotification[] suivants = StatutNotification.PROGRAMMEE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.EN_ATTENTE,
          StatutNotification.EN_COURS_ENVOI,
          StatutNotification.ANNULEE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - EN_ATTENTE")
    void testGetStatutsSuivantsPossiblesEnAttente() {
      StatutNotification[] suivants = StatutNotification.EN_ATTENTE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.EN_COURS_ENVOI,
          StatutNotification.ECHEC_ENVOI,
          StatutNotification.ANNULEE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - EN_COURS_ENVOI")
    void testGetStatutsSuivantsPossiblesEnCoursEnvoi() {
      StatutNotification[] suivants = StatutNotification.EN_COURS_ENVOI.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.ENVOYEE,
          StatutNotification.PARTIELLEMENT_ENVOYEE,
          StatutNotification.ECHEC_ENVOI);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - ENVOYEE")
    void testGetStatutsSuivantsPossiblesEnvoyee() {
      StatutNotification[] suivants = StatutNotification.ENVOYEE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.RECUE,
          StatutNotification.ECHEC_ENVOI);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - RECUE")
    void testGetStatutsSuivantsPossiblesRecue() {
      StatutNotification[] suivants = StatutNotification.RECUE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.AFFICHEE,
          StatutNotification.IGNOREE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - AFFICHEE")
    void testGetStatutsSuivantsPossiblesAffichee() {
      StatutNotification[] suivants = StatutNotification.AFFICHEE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.OUVERTE,
          StatutNotification.LUE,
          StatutNotification.NON_LUE,
          StatutNotification.IGNOREE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - OUVERTE")
    void testGetStatutsSuivantsPossiblesOuverte() {
      StatutNotification[] suivants = StatutNotification.OUVERTE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.LUE,
          StatutNotification.ACTION_EXECUTEE,
          StatutNotification.MARQUEE_IMPORTANTE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - NON_LUE")
    void testGetStatutsSuivantsPossiblesNonLue() {
      StatutNotification[] suivants = StatutNotification.NON_LUE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.LUE,
          StatutNotification.OUVERTE,
          StatutNotification.SUPPRIMEE,
          StatutNotification.ARCHIVEE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - LUE")
    void testGetStatutsSuivantsPossiblesLue() {
      StatutNotification[] suivants = StatutNotification.LUE.getStatutsSuivantsPossibles();
      assertThat(suivants).containsExactlyInAnyOrder(
          StatutNotification.ACTION_EXECUTEE,
          StatutNotification.MARQUEE_IMPORTANTE,
          StatutNotification.SUPPRIMEE,
          StatutNotification.ARCHIVEE);
    }

    @Test
    @DisplayName("getStatutsSuivantsPossibles - statuts non couverts (default)")
    void testGetStatutsSuivantsPossiblesDefault() {
      // Les statuts non couverts par le switch retournent un tableau vide
      StatutNotification[] suivants = StatutNotification.SUPPRIMEE.getStatutsSuivantsPossibles();
      assertThat(suivants).isEmpty();
      
      suivants = StatutNotification.ARCHIVEE.getStatutsSuivantsPossibles();
      assertThat(suivants).isEmpty();
      
      suivants = StatutNotification.EXPIREE.getStatutsSuivantsPossibles();
      assertThat(suivants).isEmpty();
    }
  }

  @Nested
  @DisplayName("Tests méthodes statiques")
  class MethodesStatiquesTests {

    @Test
    @DisplayName("parCode - trouve le statut par code")
    void testParCode() {
      assertThat(StatutNotification.parCode("sent")).isEqualTo(StatutNotification.ENVOYEE);
      assertThat(StatutNotification.parCode("read")).isEqualTo(StatutNotification.LUE);
    }

    @Test
    @DisplayName("parCode - code inexistant retourne null")
    void testParCodeInexistant() {
      assertThat(StatutNotification.parCode("inexistant")).isNull();
      assertThat(StatutNotification.parCode("")).isNull();
      assertThat(StatutNotification.parCode(null)).isNull();
    }

    @Test
    @DisplayName("getStatutsVisibles - retourne les statuts visibles")
    void testGetStatutsVisibles() {
      StatutNotification[] statuts = StatutNotification.getStatutsVisibles();
      assertThat(statuts).isNotEmpty();
      assertThat(statuts).allMatch(StatutNotification::isVisibleUtilisateur);
    }

    @Test
    @DisplayName("getStatutsErreur - retourne les statuts d'erreur")
    void testGetStatutsErreur() {
      StatutNotification[] statuts = StatutNotification.getStatutsErreur();
      assertThat(statuts).isNotEmpty();
      assertThat(statuts).allMatch(StatutNotification::isErreur);
    }
  }
}

