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 CanalNotification")
class CanalNotificationTest {

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

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

    @ParameterizedTest
    @EnumSource(CanalNotification.class)
    @DisplayName("Test getters de base pour toutes les valeurs")
    void testGettersBase(CanalNotification canal) {
      assertThat(canal.getId()).isNotNull().isNotEmpty();
      assertThat(canal.getNom()).isNotNull().isNotEmpty();
      assertThat(canal.getDescription()).isNotNull().isNotEmpty();
      assertThat(canal.getImportance()).isBetween(1, 5);
      assertThat(canal.getCouleur()).isNotNull().matches("#[0-9A-Fa-f]{6}");
      assertThat(canal.getTypeDefaut()).isNotNull().isNotEmpty();
    }

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, true",
        "ERROR_CHANNEL, true",
        "WARNING_CHANNEL, true",
        "IMPORTANT_CHANNEL, true",
        "REMINDER_CHANNEL, true",
        "SUCCESS_CHANNEL, false",
        "CELEBRATION_CHANNEL, false",
        "DEFAULT_CHANNEL, false"
    })
    @DisplayName("isSonActive - canaux avec son activé")
    void testIsSonActive(CanalNotification canal, Boolean expected) {
      assertThat(canal.isSonActive()).isEqualTo(expected);
    }

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, true",
        "ERROR_CHANNEL, true",
        "WARNING_CHANNEL, true",
        "IMPORTANT_CHANNEL, true",
        "REMINDER_CHANNEL, true",
        "SUCCESS_CHANNEL, false",
        "CELEBRATION_CHANNEL, false",
        "DEFAULT_CHANNEL, false"
    })
    @DisplayName("isVibrationActive - canaux avec vibration activée")
    void testIsVibrationActive(CanalNotification canal, Boolean expected) {
      assertThat(canal.isVibrationActive()).isEqualTo(expected);
    }

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, true",
        "ERROR_CHANNEL, false",
        "WARNING_CHANNEL, false",
        "IMPORTANT_CHANNEL, false",
        "REMINDER_CHANNEL, false",
        "SUCCESS_CHANNEL, false",
        "CELEBRATION_CHANNEL, false",
        "DEFAULT_CHANNEL, false"
    })
    @DisplayName("isLumiereLED - canaux avec lumière LED activée")
    void testIsLumiereLED(CanalNotification canal, Boolean expected) {
      assertThat(canal.isLumiereLED()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isCritique")
  class IsCritiqueTests {

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, true",
        "ERROR_CHANNEL, true",
        "WARNING_CHANNEL, true",
        "IMPORTANT_CHANNEL, true",
        "REMINDER_CHANNEL, false",
        "SUCCESS_CHANNEL, false",
        "DEFAULT_CHANNEL, false"
    })
    @DisplayName("isCritique - importance >= 4")
    void testIsCritique(CanalNotification canal, Boolean expected) {
      assertThat(canal.isCritique()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isSilencieux")
  class IsSilencieuxTests {

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, false",
        "EVENTS_CHANNEL, false",
        "SUCCESS_CHANNEL, true",
        "CELEBRATION_CHANNEL, true",
        "DEFAULT_CHANNEL, true"
    })
    @DisplayName("isSilencieux - pas de son ni vibration")
    void testIsSilencieux(CanalNotification canal, Boolean expected) {
      assertThat(canal.isSilencieux()).isEqualTo(expected);
    }

    @Test
    @DisplayName("isSilencieux true quand son et vibration désactivés")
    void testIsSilencieuxTrue() {
      assertThat(CanalNotification.SUCCESS_CHANNEL.isSilencieux()).isTrue();
      assertThat(CanalNotification.DEFAULT_CHANNEL.isSilencieux()).isTrue();
    }

    @Test
    @DisplayName("isSilencieux false quand son ou vibration activé")
    void testIsSilencieuxFalse() {
      assertThat(CanalNotification.URGENT_CHANNEL.isSilencieux()).isFalse();
      assertThat(CanalNotification.REMINDER_CHANNEL.isSilencieux()).isFalse();
    }
  }

  @Nested
  @DisplayName("Tests getImportanceAndroid")
  class GetImportanceAndroidTests {

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, 5, IMPORTANCE_MAX",
        "ERROR_CHANNEL, 4, IMPORTANCE_HIGH",
        "WARNING_CHANNEL, 4, IMPORTANCE_HIGH",
        "IMPORTANT_CHANNEL, 4, IMPORTANCE_HIGH",
        "REMINDER_CHANNEL, 3, IMPORTANCE_DEFAULT",
        "SUCCESS_CHANNEL, 2, IMPORTANCE_LOW",
        "CELEBRATION_CHANNEL, 2, IMPORTANCE_LOW",
        "DEFAULT_CHANNEL, 2, IMPORTANCE_LOW"
    })
    @DisplayName("getImportanceAndroid - mapping correct")
    void testGetImportanceAndroid(CanalNotification canal, int importance, String expected) {
      assertThat(canal.getImportance()).isEqualTo(importance);
      assertThat(canal.getImportanceAndroid()).isEqualTo(expected);
    }

    @Test
    @DisplayName("getImportanceAndroid - importance 1 = IMPORTANCE_MIN")
    void testGetImportanceAndroidMin() {
      // Tester avec la réflexion pour créer un canal avec importance 1
      try {
        java.lang.reflect.Field importanceField = CanalNotification.class.getDeclaredField("importance");
        importanceField.setAccessible(true);
        int importanceOriginale = (int) importanceField.get(CanalNotification.DEFAULT_CHANNEL);

        // Modifier temporairement l'importance à 1
        importanceField.setInt(CanalNotification.DEFAULT_CHANNEL, 1);

        // Maintenant importance = 1 devrait retourner IMPORTANCE_MIN
        assertThat(CanalNotification.DEFAULT_CHANNEL.getImportanceAndroid()).isEqualTo("IMPORTANCE_MIN");

        // Restaurer l'importance originale
        importanceField.setInt(CanalNotification.DEFAULT_CHANNEL, importanceOriginale);
      } catch (Exception e) {
        org.junit.jupiter.api.Assumptions.assumeTrue(false, "Réflexion non disponible");
      }
    }

    @Test
    @DisplayName("getImportanceAndroid - importance invalide = IMPORTANCE_DEFAULT")
    void testGetImportanceAndroidDefault() {
      // Tester avec la réflexion pour créer un canal avec importance invalide
      try {
        java.lang.reflect.Field importanceField = CanalNotification.class.getDeclaredField("importance");
        importanceField.setAccessible(true);
        int importanceOriginale = (int) importanceField.get(CanalNotification.DEFAULT_CHANNEL);

        // Modifier temporairement l'importance à 99 (invalide)
        importanceField.setInt(CanalNotification.DEFAULT_CHANNEL, 99);

        // Maintenant importance = 99 devrait retourner IMPORTANCE_DEFAULT (default)
        assertThat(CanalNotification.DEFAULT_CHANNEL.getImportanceAndroid()).isEqualTo("IMPORTANCE_DEFAULT");

        // Restaurer l'importance originale
        importanceField.setInt(CanalNotification.DEFAULT_CHANNEL, importanceOriginale);
      } catch (Exception e) {
        org.junit.jupiter.api.Assumptions.assumeTrue(false, "Réflexion non disponible");
      }
    }
  }

  @Nested
  @DisplayName("Tests getPrioriteIOS")
  class GetPrioriteIOSTests {

    @Test
    @DisplayName("getPrioriteIOS - importance >= 4 = high")
    void testGetPrioriteIOSHigh() {
      CanalNotification canal = CanalNotification.URGENT_CHANNEL;
      assertThat(canal.getPrioriteIOS()).isEqualTo("high");
    }

    @Test
    @DisplayName("getPrioriteIOS - importance < 4 = low")
    void testGetPrioriteIOSLow() {
      CanalNotification canal = CanalNotification.DEFAULT_CHANNEL;
      assertThat(canal.getPrioriteIOS()).isEqualTo("low");
    }
  }

  @Nested
  @DisplayName("Tests getSonDefaut")
  class GetSonDefautTests {

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, urgent_sound.mp3",
        "ERROR_CHANNEL, error_sound.mp3",
        "WARNING_CHANNEL, warning_sound.mp3",
        "IMPORTANT_CHANNEL, important_sound.mp3",
        "REMINDER_CHANNEL, reminder_sound.mp3",
        "SUCCESS_CHANNEL, success_sound.mp3",
        "CELEBRATION_CHANNEL, celebration_sound.mp3",
        "DEFAULT_CHANNEL, default"
    })
    @DisplayName("getSonDefaut - tous les canaux")
    void testGetSonDefaut(CanalNotification canal, String expected) {
      assertThat(canal.getSonDefaut()).isEqualTo(expected);
    }

    @Test
    @DisplayName("getSonDefaut - canaux catégoriels (default)")
    void testGetSonDefautCanauxCategories() {
      assertThat(CanalNotification.EVENTS_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.PAYMENTS_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.SOLIDARITY_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.MEMBERS_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.ORGANIZATION_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.SYSTEM_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.MESSAGES_CHANNEL.getSonDefaut()).isEqualTo("default");
      assertThat(CanalNotification.LOCATION_CHANNEL.getSonDefaut()).isEqualTo("default");
    }
  }

  @Nested
  @DisplayName("Tests getPatternVibration")
  class GetPatternVibrationTests {

    @Test
    @DisplayName("getPatternVibration - URGENT_CHANNEL")
    void testGetPatternVibrationUrgent() {
      long[] pattern = CanalNotification.URGENT_CHANNEL.getPatternVibration();
      assertThat(pattern).isNotEmpty();
      assertThat(pattern[0]).isEqualTo(0);
      assertThat(pattern).hasSize(6); // Triple vibration
    }

    @Test
    @DisplayName("getPatternVibration - ERROR_CHANNEL")
    void testGetPatternVibrationError() {
      long[] pattern = CanalNotification.ERROR_CHANNEL.getPatternVibration();
      assertThat(pattern).hasSize(4); // Double vibration longue
      assertThat(pattern[0]).isEqualTo(0);
    }

    @Test
    @DisplayName("getPatternVibration - WARNING_CHANNEL")
    void testGetPatternVibrationWarning() {
      long[] pattern = CanalNotification.WARNING_CHANNEL.getPatternVibration();
      assertThat(pattern).hasSize(4); // Double vibration courte
      assertThat(pattern[0]).isEqualTo(0);
    }

    @Test
    @DisplayName("getPatternVibration - IMPORTANT_CHANNEL")
    void testGetPatternVibrationImportant() {
      long[] pattern = CanalNotification.IMPORTANT_CHANNEL.getPatternVibration();
      assertThat(pattern).hasSize(4); // Vibration distinctive
      assertThat(pattern[0]).isEqualTo(0);
    }

    @Test
    @DisplayName("getPatternVibration - REMINDER_CHANNEL")
    void testGetPatternVibrationReminder() {
      long[] pattern = CanalNotification.REMINDER_CHANNEL.getPatternVibration();
      assertThat(pattern).hasSize(4); // Vibration douce
      assertThat(pattern[0]).isEqualTo(0);
    }

    @Test
    @DisplayName("getPatternVibration - autres canaux (default)")
    void testGetPatternVibrationDefault() {
      long[] pattern = CanalNotification.SUCCESS_CHANNEL.getPatternVibration();
      assertThat(pattern).hasSize(2); // Vibration simple
      assertThat(pattern[0]).isEqualTo(0);

      pattern = CanalNotification.DEFAULT_CHANNEL.getPatternVibration();
      assertThat(pattern).hasSize(2); // Vibration simple
      assertThat(pattern[0]).isEqualTo(0);
    }

    @ParameterizedTest
    @EnumSource(CanalNotification.class)
    @DisplayName("getPatternVibration - tous les canaux retournent un pattern valide")
    void testGetPatternVibrationTousCanaux(CanalNotification canal) {
      long[] pattern = canal.getPatternVibration();
      assertThat(pattern).isNotNull();
      assertThat(pattern.length).isGreaterThan(0);
      assertThat(pattern[0]).isEqualTo(0);
    }
  }

  @Nested
  @DisplayName("Tests peutEtreDesactive")
  class PeutEtreDesactiveTests {

    @Test
    @DisplayName("peutEtreDesactive - URGENT_CHANNEL ne peut pas être désactivé")
    void testPeutEtreDesactiveUrgent() {
      assertThat(CanalNotification.URGENT_CHANNEL.peutEtreDesactive()).isFalse();
    }

    @Test
    @DisplayName("peutEtreDesactive - ERROR_CHANNEL ne peut pas être désactivé")
    void testPeutEtreDesactiveError() {
      assertThat(CanalNotification.ERROR_CHANNEL.peutEtreDesactive()).isFalse();
    }

    @Test
    @DisplayName("peutEtreDesactive - autres canaux peuvent être désactivés")
    void testPeutEtreDesactiveAutres() {
      assertThat(CanalNotification.DEFAULT_CHANNEL.peutEtreDesactive()).isTrue();
      assertThat(CanalNotification.SUCCESS_CHANNEL.peutEtreDesactive()).isTrue();
    }
  }

  @Nested
  @DisplayName("Tests getDureeVieMs")
  class GetDureeVieMsTests {

    @ParameterizedTest
    @CsvSource({
        "URGENT_CHANNEL, 3600000",
        "ERROR_CHANNEL, 86400000",
        "WARNING_CHANNEL, 172800000",
        "IMPORTANT_CHANNEL, 259200000",
        "REMINDER_CHANNEL, 86400000",
        "SUCCESS_CHANNEL, 172800000",
        "CELEBRATION_CHANNEL, 259200000",
        "DEFAULT_CHANNEL, 604800000"
    })
    @DisplayName("getDureeVieMs - toutes les durées")
    void testGetDureeVieMs(CanalNotification canal, long expectedMs) {
      assertThat(canal.getDureeVieMs()).isEqualTo(expectedMs);
    }

    @Test
    @DisplayName("getDureeVieMs - canaux catégoriels (default = 1 semaine)")
    void testGetDureeVieMsCanauxCategories() {
      assertThat(CanalNotification.EVENTS_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.PAYMENTS_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.SOLIDARITY_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.MEMBERS_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.ORGANIZATION_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.SYSTEM_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.MESSAGES_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
      assertThat(CanalNotification.LOCATION_CHANNEL.getDureeVieMs()).isEqualTo(604800000L);
    }
  }

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

    @Test
    @DisplayName("parId - trouve le canal")
    void testParId() {
      assertThat(CanalNotification.parId("urgent")).isEqualTo(CanalNotification.URGENT_CHANNEL);
      assertThat(CanalNotification.parId("default")).isEqualTo(CanalNotification.DEFAULT_CHANNEL);
    }

    @Test
    @DisplayName("parId - id inexistant retourne DEFAULT_CHANNEL")
    void testParIdInexistant() {
      assertThat(CanalNotification.parId("inexistant")).isEqualTo(CanalNotification.DEFAULT_CHANNEL);
    }

    @Test
    @DisplayName("getCanauxCritiques - retourne les canaux critiques")
    void testGetCanauxCritiques() {
      CanalNotification[] canaux = CanalNotification.getCanauxCritiques();
      assertThat(canaux).hasSize(4);
      assertThat(canaux).containsExactly(
          CanalNotification.URGENT_CHANNEL,
          CanalNotification.ERROR_CHANNEL,
          CanalNotification.WARNING_CHANNEL,
          CanalNotification.IMPORTANT_CHANNEL);
    }

    @Test
    @DisplayName("getCanauxCategories - retourne les canaux catégoriels")
    void testGetCanauxCategories() {
      CanalNotification[] canaux = CanalNotification.getCanauxCategories();
      assertThat(canaux).hasSize(8);
      assertThat(canaux).contains(
          CanalNotification.EVENTS_CHANNEL,
          CanalNotification.PAYMENTS_CHANNEL,
          CanalNotification.SOLIDARITY_CHANNEL);
    }
  }
}
