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

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

import java.lang.reflect.Field;
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 TypeMetrique")
class TypeMetriqueTest {

  @Test
  @DisplayName("Test de base - enum peut être utilisé")
  void testEnumUtilisable() {
    assertThat(TypeMetrique.NOMBRE_MEMBRES_ACTIFS).isNotNull();
  }

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

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

    @ParameterizedTest
    @EnumSource(TypeMetrique.class)
    @DisplayName("Test getters de base pour toutes les valeurs")
    void testGettersBase(TypeMetrique metrique) {
      assertThat(metrique.getLibelle()).isNotNull().isNotEmpty();
      assertThat(metrique.getCategorie()).isNotNull().isNotEmpty();
      assertThat(metrique.getTypeValeur()).isNotNull().isNotEmpty();
    }
  }

  @Nested
  @DisplayName("Tests isFinanciere")
  class IsFinanciereTests {

    @ParameterizedTest
    @CsvSource({
        "TOTAL_COTISATIONS_COLLECTEES, true",
        "COTISATIONS_EN_ATTENTE, true",
        "TAUX_RECOUVREMENT_COTISATIONS, true",
        "NOMBRE_MEMBRES_ACTIFS, false",
        "NOMBRE_EVENEMENTS_ORGANISES, false"
    })
    @DisplayName("isFinanciere - métriques financières")
    void testIsFinanciere(TypeMetrique metrique, Boolean expected) {
      assertThat(metrique.isFinanciere()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isPourcentage")
  class IsPourcentageTests {

    @ParameterizedTest
    @CsvSource({
        "TAUX_CROISSANCE_MEMBRES, true",
        "TAUX_RECOUVREMENT_COTISATIONS, true",
        "TAUX_PARTICIPATION_EVENEMENTS, true",
        "NOMBRE_MEMBRES_ACTIFS, false",
        "TOTAL_COTISATIONS_COLLECTEES, false"
    })
    @DisplayName("isPourcentage - métriques en pourcentage")
    void testIsPourcentage(TypeMetrique metrique, Boolean expected) {
      assertThat(metrique.isPourcentage()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests isCompteur")
  class IsCompteurTests {

    @ParameterizedTest
    @CsvSource({
        "NOMBRE_MEMBRES_ACTIFS, true",
        "NOMBRE_EVENEMENTS_ORGANISES, true",
        "NOMBRE_DEMANDES_AIDE, true",
        "TAUX_CROISSANCE_MEMBRES, false",
        "TOTAL_COTISATIONS_COLLECTEES, false"
    })
    @DisplayName("isCompteur - métriques compteurs")
    void testIsCompteur(TypeMetrique metrique, Boolean expected) {
      assertThat(metrique.isCompteur()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests getUnite")
  class GetUniteTests {

    @ParameterizedTest
    @CsvSource({
        "TAUX_CROISSANCE_MEMBRES, %",
        "TOTAL_COTISATIONS_COLLECTEES, XOF",
        "DELAI_TRAITEMENT_DEMANDES, jours",
        "UTILISATION_STOCKAGE, MB",
        "FREQUENCE_UTILISATION_APP, /jour",
        "NPS_SATISFACTION, /10",
        "NOMBRE_MEMBRES_ACTIFS,"
    })
    @DisplayName("getUnite - toutes les unités")
    void testGetUnite(TypeMetrique metrique, String expected) {
      if (expected == null || expected.isEmpty()) {
        assertThat(metrique.getUnite()).isEmpty();
      } else {
        assertThat(metrique.getUnite()).isEqualTo(expected);
      }
    }
  }

  @Nested
  @DisplayName("Tests getIcone")
  class GetIconeTests {

    @ParameterizedTest
    @CsvSource({
        "NOMBRE_MEMBRES_ACTIFS, people",
        "TOTAL_COTISATIONS_COLLECTEES, attach_money",
        "NOMBRE_EVENEMENTS_ORGANISES, event",
        "NOMBRE_DEMANDES_AIDE, favorite",
        "TAUX_CONNEXION_MOBILE, trending_up",
        "NOMBRE_ORGANISATIONS_ACTIVES, business",
        "TEMPS_REPONSE_API, settings"
    })
    @DisplayName("getIcone - toutes les icônes")
    void testGetIcone(TypeMetrique metrique, String expected) {
      assertThat(metrique.getIcone()).isEqualTo(expected);
    }
  }

  @Nested
  @DisplayName("Tests getCouleur")
  class GetCouleurTests {

    @ParameterizedTest
    @CsvSource({
        "NOMBRE_MEMBRES_ACTIFS, #2196F3",
        "TOTAL_COTISATIONS_COLLECTEES, #4CAF50",
        "NOMBRE_EVENEMENTS_ORGANISES, #FF9800",
        "NOMBRE_DEMANDES_AIDE, #E91E63",
        "TAUX_CONNEXION_MOBILE, #9C27B0",
        "NOMBRE_ORGANISATIONS_ACTIVES, #607D8B",
        "TEMPS_REPONSE_API, #795548"
    })
    @DisplayName("getCouleur - toutes les couleurs")
    void testGetCouleur(TypeMetrique metrique, String expected) {
      assertThat(metrique.getCouleur()).isEqualTo(expected);
    }

    @Test
    @DisplayName("getUnite - cas default (typeValeur non reconnu)")
    void testGetUniteDefault() {
      // Test les métriques avec typeValeur qui tombent dans le default (count, average, distribution, trend)
      assertThat(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getUnite()).isEmpty(); // count -> default
      assertThat(TypeMetrique.NOMBRE_MEMBRES_INACTIFS.getUnite()).isEmpty(); // count -> default
      assertThat(TypeMetrique.MOYENNE_AGE_MEMBRES.getUnite()).isEmpty(); // average -> default
      assertThat(TypeMetrique.MOYENNE_COTISATION_MEMBRE.getUnite()).isEmpty(); // average -> default
      assertThat(TypeMetrique.MOYENNE_PARTICIPANTS_EVENEMENT.getUnite()).isEmpty(); // average -> default
      assertThat(TypeMetrique.MOYENNE_MEMBRES_PAR_ORGANISATION.getUnite()).isEmpty(); // average -> default
      assertThat(TypeMetrique.REPARTITION_GENRE_MEMBRES.getUnite()).isEmpty(); // distribution -> default
      assertThat(TypeMetrique.EVOLUTION_REVENUS_MENSUELLE.getUnite()).isEmpty(); // trend -> default
      
      // Test aussi les autres métriques avec count
      assertThat(TypeMetrique.NOMBRE_EVENEMENTS_ORGANISES.getUnite()).isEmpty();
      assertThat(TypeMetrique.EVENEMENTS_ANNULES.getUnite()).isEmpty();
      assertThat(TypeMetrique.NOMBRE_DEMANDES_AIDE.getUnite()).isEmpty();
      assertThat(TypeMetrique.ACTIONS_UTILISATEUR_JOUR.getUnite()).isEmpty();
      assertThat(TypeMetrique.NOMBRE_ORGANISATIONS_ACTIVES.getUnite()).isEmpty();
      assertThat(TypeMetrique.ORGANISATIONS_PREMIUM.getUnite()).isEmpty();
      assertThat(TypeMetrique.NOMBRE_ERREURS_SYSTEME.getUnite()).isEmpty();
    }

    @Test
    @DisplayName("Test toutes les métriques - couverture complète")
    void testToutesLesMetriques() {
      // Test toutes les métriques pour s'assurer qu'elles sont toutes couvertes
      for (TypeMetrique metrique : TypeMetrique.values()) {
        assertThat(metrique.getLibelle()).isNotNull().isNotEmpty();
        assertThat(metrique.getCategorie()).isNotNull().isNotEmpty();
        assertThat(metrique.getTypeValeur()).isNotNull().isNotEmpty();
        assertThat(metrique.getUnite()).isNotNull();
        assertThat(metrique.getIcone()).isNotNull().isNotEmpty();
        assertThat(metrique.getCouleur()).isNotNull().isNotEmpty();
      }
    }

    @Test
    @DisplayName("getIcone - cas default (catégorie non reconnue)")
    void testGetIconeDefault() throws Exception {
      // Utiliser la réflexion pour modifier temporairement la catégorie
      // et tester le cas default
      TypeMetrique metrique = TypeMetrique.NOMBRE_MEMBRES_ACTIFS;
      String categorieOriginale = metrique.getCategorie();

      try {
        // Modifier la catégorie via réflexion pour une catégorie non reconnue
        Field categorieField = TypeMetrique.class.getDeclaredField("categorie");
        categorieField.setAccessible(true);
        categorieField.set(metrique, "categorie_inconnue");

        // Tester que le default retourne "analytics"
        assertThat(metrique.getIcone()).isEqualTo("analytics");
      } finally {
        // Restaurer la valeur originale
        Field categorieField = TypeMetrique.class.getDeclaredField("categorie");
        categorieField.setAccessible(true);
        categorieField.set(metrique, categorieOriginale);
      }
    }

    @Test
    @DisplayName("getCouleur - cas default (catégorie non reconnue)")
    void testGetCouleurDefault() throws Exception {
      // Utiliser la réflexion pour modifier temporairement la catégorie
      // et tester le cas default
      TypeMetrique metrique = TypeMetrique.NOMBRE_MEMBRES_ACTIFS;
      String categorieOriginale = metrique.getCategorie();

      try {
        // Modifier la catégorie via réflexion pour une catégorie non reconnue
        Field categorieField = TypeMetrique.class.getDeclaredField("categorie");
        categorieField.setAccessible(true);
        categorieField.set(metrique, "categorie_inconnue");

        // Tester que le default retourne "#757575"
        assertThat(metrique.getCouleur()).isEqualTo("#757575");
      } finally {
        // Restaurer la valeur originale
        Field categorieField = TypeMetrique.class.getDeclaredField("categorie");
        categorieField.setAccessible(true);
        categorieField.set(metrique, categorieOriginale);
      }
    }
  }
}

