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

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

import java.time.LocalTime;
import java.util.Set;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import dev.lions.unionflow.server.api.enums.notification.CanalNotification;
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;

/**
 * Tests unitaires pour PreferencesNotificationDTO.
 * Couvre constructeur(String), isTypeActive (toutes branches), isCanalActif (toutes branches),
 * isEnModeSilencieux(LocalTime) (traverse minuit, plage normale, null),
 * isExpediteurBloque, isExpediteurPrioritaire, toString.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-21
 */
@DisplayName("Tests PreferencesNotificationDTO")
class PreferencesNotificationDTOTest {

  @Nested
  @DisplayName("Constructeurs")
  class Constructeurs {

    @Test
    @DisplayName("constructeur par défaut initialise les champs")
    void testConstructeurParDefaut() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      assertThat(dto.getNotificationsActivees()).isTrue();
      assertThat(dto.getPushActivees()).isTrue();
      assertThat(dto.getEmailActivees()).isTrue();
      assertThat(dto.getSmsActivees()).isFalse();
      assertThat(dto.getInAppActivees()).isTrue();
      assertThat(dto.getModeSilencieux()).isFalse();
      assertThat(dto.getFrequenceRegroupementMinutes()).isEqualTo(5);
      assertThat(dto.getMaxNotificationsSimultanees()).isEqualTo(10);
      assertThat(dto.getDureeAffichageSecondes()).isEqualTo(10);
      assertThat(dto.getLangue()).isEqualTo("fr");
    }

    @Test
    @DisplayName("constructeur avec utilisateurId appelle this() et set utilisateurId")
    void testConstructeurAvecUtilisateur() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO("user-123");
      assertThat(dto.getUtilisateurId()).isEqualTo("user-123");
      assertThat(dto.getNotificationsActivees()).isTrue();
    }
  }

  @Nested
  @DisplayName("isTypeActive")
  class IsTypeActive {

    @Test
    @DisplayName("retourne false quand notificationsActivees false")
    void testNotificationsDesactivees() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setNotificationsActivees(false);
      assertThat(dto.isTypeActive(TypeNotification.EMAIL)).isFalse();
    }

    @Test
    @DisplayName("retourne false quand type dans typesDesactivees")
    void testTypeDesactive() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setTypesDesactivees(Set.of(TypeNotification.EMAIL));
      assertThat(dto.isTypeActive(TypeNotification.EMAIL)).isFalse();
    }

    @Test
    @DisplayName("retourne true quand type dans typesActives")
    void testTypeActif() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setTypesActives(Set.of(TypeNotification.EMAIL));
      assertThat(dto.isTypeActive(TypeNotification.EMAIL)).isTrue();
    }

    @Test
    @DisplayName("retourne type.isActiveeParDefaut() quand typesActives null et type pas dans typesDesactivees")
    void testDefautType() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setTypesActives(null);
      dto.setTypesDesactivees(null);
      TypeNotification type = TypeNotification.EMAIL;
      assertThat(dto.isTypeActive(type)).isEqualTo(type.isActiveeParDefaut());
    }

    @Test
    @DisplayName("retourne false quand type pas dans typesActives (typesActives non vide)")
    void testTypePasDansActives() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setTypesActives(Set.of(TypeNotification.IN_APP));
      assertThat(dto.isTypeActive(TypeNotification.EMAIL)).isFalse();
    }
  }

  @Nested
  @DisplayName("isCanalActif")
  class IsCanalActif {

    @Test
    @DisplayName("retourne false quand notificationsActivees false")
    void testNotificationsDesactivees() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setNotificationsActivees(false);
      assertThat(dto.isCanalActif(CanalNotification.DEFAULT_CHANNEL)).isFalse();
    }

    @Test
    @DisplayName("retourne false quand canal dans canauxDesactives")
    void testCanalDesactive() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setCanauxDesactives(Set.of(CanalNotification.DEFAULT_CHANNEL));
      assertThat(dto.isCanalActif(CanalNotification.DEFAULT_CHANNEL)).isFalse();
    }

    @Test
    @DisplayName("retourne true quand canal dans canauxActifs")
    void testCanalActif() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setCanauxActifs(Set.of(CanalNotification.DEFAULT_CHANNEL));
      assertThat(dto.isCanalActif(CanalNotification.DEFAULT_CHANNEL)).isTrue();
    }

    @Test
    @DisplayName("retourne true quand canauxActifs null (défaut)")
    void testCanauxActifsNull() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setCanauxActifs(null);
      dto.setCanauxDesactives(null);
      assertThat(dto.isCanalActif(CanalNotification.DEFAULT_CHANNEL)).isTrue();
    }

    @Test
    @DisplayName("retourne false quand canal pas dans canauxActifs (canauxActifs non vide)")
    void testCanalPasDansActifs() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setCanauxActifs(Set.of(CanalNotification.REMINDER_CHANNEL));
      assertThat(dto.isCanalActif(CanalNotification.DEFAULT_CHANNEL)).isFalse();
    }
  }

  @Nested
  @DisplayName("isEnModeSilencieux (package)")
  class IsEnModeSilencieux {

    @Test
    @DisplayName("retourne false quand modeSilencieux false")
    void testModeDesactive() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setModeSilencieux(false);
      assertThat(dto.isEnModeSilencieux(LocalTime.NOON)).isFalse();
    }

    @Test
    @DisplayName("retourne false quand heureDebut ou heureFin null")
    void testHeuresNull() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setModeSilencieux(true);
      dto.setHeureDebutSilencieux(null);
      dto.setHeureFinSilencieux(LocalTime.of(18, 0));
      assertThat(dto.isEnModeSilencieux(LocalTime.NOON)).isFalse();
      dto.setHeureDebutSilencieux(LocalTime.of(22, 0));
      dto.setHeureFinSilencieux(null);
      assertThat(dto.isEnModeSilencieux(LocalTime.of(23, 0))).isFalse();
    }

    @Test
    @DisplayName("période normale (début < fin): true dans la plage")
    void testPlageNormaleDansPlage() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setModeSilencieux(true);
      dto.setHeureDebutSilencieux(LocalTime.of(9, 0));
      dto.setHeureFinSilencieux(LocalTime.of(12, 0));
      assertThat(dto.isEnModeSilencieux(LocalTime.of(10, 30))).isTrue();
    }

    @Test
    @DisplayName("période normale: false en dehors de la plage")
    void testPlageNormaleHorsPlage() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setModeSilencieux(true);
      dto.setHeureDebutSilencieux(LocalTime.of(9, 0));
      dto.setHeureFinSilencieux(LocalTime.of(12, 0));
      assertThat(dto.isEnModeSilencieux(LocalTime.of(8, 0))).isFalse();
      assertThat(dto.isEnModeSilencieux(LocalTime.of(13, 0))).isFalse();
      assertThat(dto.isEnModeSilencieux(LocalTime.of(9, 0))).isFalse();
      assertThat(dto.isEnModeSilencieux(LocalTime.of(12, 0))).isFalse();
    }

    @Test
    @DisplayName("période traverse minuit (début > fin): true après début ou avant fin")
    void testTraverseMinuit() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setModeSilencieux(true);
      dto.setHeureDebutSilencieux(LocalTime.of(22, 0));
      dto.setHeureFinSilencieux(LocalTime.of(7, 0));
      assertThat(dto.isEnModeSilencieux(LocalTime.of(23, 0))).isTrue();
      assertThat(dto.isEnModeSilencieux(LocalTime.of(2, 0))).isTrue();
    }

    @Test
    @DisplayName("période traverse minuit: false entre fin et début")
    void testTraverseMinuitHorsPlage() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setModeSilencieux(true);
      dto.setHeureDebutSilencieux(LocalTime.of(22, 0));
      dto.setHeureFinSilencieux(LocalTime.of(7, 0));
      assertThat(dto.isEnModeSilencieux(LocalTime.of(10, 0))).isFalse();
    }
  }

  @Nested
  @DisplayName("isExpediteurBloque")
  class IsExpediteurBloque {

    @Test
    @DisplayName("retourne false quand expediteursBloqués null")
    void testNull() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setExpediteursBloques(null);
      assertThat(dto.isExpediteurBloque("exp-1")).isFalse();
    }

    @Test
    @DisplayName("retourne true quand expediteur dans la liste")
    void testBloque() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setExpediteursBloques(Set.of("exp-1", "exp-2"));
      assertThat(dto.isExpediteurBloque("exp-1")).isTrue();
    }

    @Test
    @DisplayName("retourne false quand expediteur pas dans la liste")
    void testPasBloque() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setExpediteursBloques(Set.of("exp-1"));
      assertThat(dto.isExpediteurBloque("exp-2")).isFalse();
    }
  }

  @Nested
  @DisplayName("isExpediteurPrioritaire")
  class IsExpediteurPrioritaire {

    @Test
    @DisplayName("retourne false quand expediteursPrioritaires null")
    void testNull() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setExpediteursPrioritaires(null);
      assertThat(dto.isExpediteurPrioritaire("exp-1")).isFalse();
    }

    @Test
    @DisplayName("retourne true quand expediteur dans la liste")
    void testPrioritaire() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setExpediteursPrioritaires(Set.of("exp-1"));
      assertThat(dto.isExpediteurPrioritaire("exp-1")).isTrue();
    }

    @Test
    @DisplayName("retourne false quand expediteur pas dans la liste")
    void testPasPrioritaire() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO();
      dto.setExpediteursPrioritaires(Set.of("exp-1"));
      assertThat(dto.isExpediteurPrioritaire("exp-2")).isFalse();
    }
  }

  @Nested
  @DisplayName("toString")
  class ToString {

    @Test
    @DisplayName("contient utilisateurId et notificationsActivees")
    void testToString() {
      PreferencesNotificationDTO dto = new PreferencesNotificationDTO("u1");
      String s = dto.toString();
      assertThat(s).contains("u1");
      assertThat(s).contains("true");
    }
  }
}
