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

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 dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;

/**
 * Tests unitaires pour DashboardWidgetResponse.
 * Couvre getLibelleMetrique, getUnite, getIconeAffichage, getCouleurAffichage,
 * necessiteMiseAJour, isInteractif, isTempsReel, getTailleWidget, isWidgetGrand,
 * hasErreursRecentes, getStatutWidget.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-21
 */
@DisplayName("Tests DashboardWidgetResponse")
class DashboardWidgetResponseTest {

  private static DashboardWidgetResponse minimalWidget() {
    return DashboardWidgetResponse.builder()
        .titre("Widget")
        .typeWidget("kpi")
        .utilisateurProprietaireId(UUID.randomUUID())
        .positionX(0)
        .positionY(0)
        .largeur(2)
        .hauteur(2).build();
  }

  @Nested
  @DisplayName("getLibelleMetrique")
  class GetLibelleMetrique {

    @Test
    @DisplayName("retourne null quand typeMetrique null")
    void testNull() {
      DashboardWidgetResponse w = minimalWidget();
      assertThat(w.getLibelleMetrique()).isNull();
    }

    @Test
    @DisplayName("retourne typeMetrique.getLibelle() quand défini")
    void testLibelle() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
      assertThat(w.getLibelleMetrique()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getLibelle());
    }
  }

  @Nested
  @DisplayName("getUnite")
  class GetUnite {

    @Test
    @DisplayName("retourne chaîne vide quand typeMetrique null")
    void testNull() {
      DashboardWidgetResponse w = minimalWidget();
      assertThat(w.getUnite()).isEmpty();
    }

    @Test
    @DisplayName("retourne typeMetrique.getUnite() quand défini")
    void testUnite() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
      assertThat(w.getUnite()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getUnite());
    }
  }

  @Nested
  @DisplayName("getIconeAffichage")
  class GetIconeAffichage {

    @Test
    @DisplayName("retourne icone personnalisée si non vide")
    void testPersonnalise() {
      DashboardWidgetResponse w = minimalWidget();
      w.setIcone("custom_icon");
      assertThat(w.getIconeAffichage()).isEqualTo("custom_icon");
    }

    @Test
    @DisplayName("retourne typeMetrique.getIcone() si icone null")
    void testTypeMetrique() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
      assertThat(w.getIconeAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getIcone());
    }

    @Test
    @DisplayName("retourne dashboard si icone vide et typeMetrique null")
    void testDefault() {
      DashboardWidgetResponse w = minimalWidget();
      w.setIcone("  ");
      assertThat(w.getIconeAffichage()).isEqualTo("dashboard");
    }

    @Test
    @DisplayName("retourne dashboard si icone null et typeMetrique null")
    void testIconeNullTypeMetriqueNull() {
      DashboardWidgetResponse w = minimalWidget();
      w.setIcone(null);
      w.setTypeMetrique(null);
      assertThat(w.getIconeAffichage()).isEqualTo("dashboard");
    }
  }

  @Nested
  @DisplayName("getCouleurAffichage")
  class GetCouleurAffichage {

    @Test
    @DisplayName("retourne couleurPrincipale si non vide")
    void testPersonnalise() {
      DashboardWidgetResponse w = minimalWidget();
      w.setCouleurPrincipale("#FF0000");
      assertThat(w.getCouleurAffichage()).isEqualTo("#FF0000");
    }

    @Test
    @DisplayName("retourne typeMetrique.getCouleur() si couleurPrincipale null")
    void testTypeMetrique() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTypeMetrique(TypeMetrique.NOMBRE_MEMBRES_ACTIFS);
      assertThat(w.getCouleurAffichage()).isEqualTo(TypeMetrique.NOMBRE_MEMBRES_ACTIFS.getCouleur());
    }

    @Test
    @DisplayName("retourne #757575 si couleurPrincipale vide et typeMetrique null")
    void testDefault() {
      DashboardWidgetResponse w = minimalWidget();
      w.setCouleurPrincipale("  ");
      assertThat(w.getCouleurAffichage()).isEqualTo("#757575");
    }

    @Test
    @DisplayName("retourne #757575 si couleurPrincipale null et typeMetrique null")
    void testCouleurNullTypeMetriqueNull() {
      DashboardWidgetResponse w = minimalWidget();
      w.setCouleurPrincipale(null);
      w.setTypeMetrique(null);
      assertThat(w.getCouleurAffichage()).isEqualTo("#757575");
    }
  }

  @Nested
  @DisplayName("necessiteMiseAJour")
  class NecessiteMiseAJour {

    @Test
    @DisplayName("retourne true quand miseAJourAutomatique et prochaineMiseAJour dans le passé")
    void testTrue() {
      DashboardWidgetResponse w = minimalWidget();
      w.setMiseAJourAutomatique(true);
      w.setProchaineMiseAJour(LocalDateTime.now().minusMinutes(5));
      assertThat(w.necessiteMiseAJour()).isTrue();
    }

    @Test
    @DisplayName("retourne false quand miseAJourAutomatique false")
    void testFalseNonAuto() {
      DashboardWidgetResponse w = minimalWidget();
      w.setMiseAJourAutomatique(false);
      w.setProchaineMiseAJour(LocalDateTime.now().minusMinutes(5));
      assertThat(w.necessiteMiseAJour()).isFalse();
    }

    @Test
    @DisplayName("retourne false quand prochaineMiseAJour null ou futur")
    void testFalseFutur() {
      DashboardWidgetResponse w = minimalWidget();
      w.setMiseAJourAutomatique(true);
      w.setProchaineMiseAJour(null);
      assertThat(w.necessiteMiseAJour()).isFalse();
      w.setProchaineMiseAJour(LocalDateTime.now().plusHours(1));
      assertThat(w.necessiteMiseAJour()).isFalse();
    }
  }

  @Nested
  @DisplayName("isInteractif")
  class IsInteractif {

    @Test
    @DisplayName("retourne true pour chart, table, gauge")
    void testInteractif() {
      assertThat(DashboardWidgetResponse.builder().titre("x").typeWidget("chart").utilisateurProprietaireId(UUID.randomUUID()).positionX(0).positionY(0).largeur(1).hauteur(1).build().isInteractif()).isTrue();
      DashboardWidgetResponse w = minimalWidget();
      w.setTypeWidget("table");
      assertThat(w.isInteractif()).isTrue();
      w.setTypeWidget("gauge");
      assertThat(w.isInteractif()).isTrue();
    }

    @Test
    @DisplayName("retourne false pour kpi, progress, text")
    void testNonInteractif() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTypeWidget("kpi");
      assertThat(w.isInteractif()).isFalse();
      w.setTypeWidget("text");
      assertThat(w.isInteractif()).isFalse();
    }
  }

  @Nested
  @DisplayName("isTempsReel")
  class IsTempsReel {

    @Test
    @DisplayName("retourne true quand frequenceMiseAJourSecondes <= 60")
    void testTempsReel() {
      DashboardWidgetResponse w = minimalWidget();
      w.setFrequenceMiseAJourSecondes(30);
      assertThat(w.isTempsReel()).isTrue();
      w.setFrequenceMiseAJourSecondes(60);
      assertThat(w.isTempsReel()).isTrue();
    }

    @Test
    @DisplayName("retourne false quand frequenceMiseAJourSecondes > 60 ou null")
    void testNonTempsReel() {
      DashboardWidgetResponse w = minimalWidget();
      assertThat(w.isTempsReel()).isFalse();
      w.setFrequenceMiseAJourSecondes(300);
      assertThat(w.isTempsReel()).isFalse();
      w.setFrequenceMiseAJourSecondes(61);
      assertThat(w.isTempsReel()).isFalse();
    }

    @Test
    @DisplayName("retourne false quand frequenceMiseAJourSecondes explicitement null")
    void testTempsReelFrequenceNull() {
      DashboardWidgetResponse w = minimalWidget();
      w.setFrequenceMiseAJourSecondes(30);
      w.setFrequenceMiseAJourSecondes(null);
      assertThat(w.isTempsReel()).isFalse();
    }
  }

  @Nested
  @DisplayName("getTailleWidget et isWidgetGrand")
  class TailleWidget {

    @Test
    @DisplayName("getTailleWidget retourne largeur * hauteur")
    void testTaille() {
      DashboardWidgetResponse w = minimalWidget();
      w.setLargeur(3);
      w.setHauteur(2);
      assertThat(w.getTailleWidget()).isEqualTo(6);
    }

    @Test
    @DisplayName("isWidgetGrand true quand surface > 6")
    void testGrand() {
      DashboardWidgetResponse w = minimalWidget();
      w.setLargeur(4);
      w.setHauteur(2);
      assertThat(w.getTailleWidget()).isEqualTo(8);
      assertThat(w.isWidgetGrand()).isTrue();
    }

    @Test
    @DisplayName("isWidgetGrand false quand surface <= 6")
    void testPetit() {
      DashboardWidgetResponse w = minimalWidget();
      assertThat(w.getTailleWidget()).isEqualTo(4);
      assertThat(w.isWidgetGrand()).isFalse();
    }
  }

  @Nested
  @DisplayName("hasErreursRecentes et getStatutWidget")
  class ErreursEtStatut {

    @Test
    @DisplayName("hasErreursRecentes true quand dateDerniereErreur < 24h")
    void testErreurRecente() {
      DashboardWidgetResponse w = minimalWidget();
      w.setDateDerniereErreur(LocalDateTime.now().minusHours(2));
      assertThat(w.hasErreursRecentes()).isTrue();
    }

    @Test
    @DisplayName("hasErreursRecentes false quand dateDerniereErreur null ou > 24h")
    void testPasErreurRecente() {
      DashboardWidgetResponse w = minimalWidget();
      assertThat(w.hasErreursRecentes()).isFalse();
      w.setDateDerniereErreur(LocalDateTime.now().minusHours(25));
      assertThat(w.hasErreursRecentes()).isFalse();
    }

    @Test
    @DisplayName("getStatutWidget retourne erreur si hasErreursRecentes")
    void testStatutErreur() {
      DashboardWidgetResponse w = minimalWidget();
      w.setDateDerniereErreur(LocalDateTime.now().minusHours(1));
      assertThat(w.getStatutWidget()).isEqualTo("erreur");
    }

    @Test
    @DisplayName("getStatutWidget retourne inactif si visible false")
    void testStatutInactif() {
      DashboardWidgetResponse w = minimalWidget();
      w.setVisible(false);
      assertThat(w.getStatutWidget()).isEqualTo("inactif");
    }

    @Test
    @DisplayName("getStatutWidget retourne maintenance si tauxErreur > 10")
    void testStatutMaintenance() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTauxErreur(15.0);
      assertThat(w.getStatutWidget()).isEqualTo("maintenance");
    }

    @Test
    @DisplayName("getStatutWidget retourne actif sinon")
    void testStatutActif() {
      DashboardWidgetResponse w = minimalWidget();
      assertThat(w.getStatutWidget()).isEqualTo("actif");
    }

    @Test
    @DisplayName("getStatutWidget retourne actif quand tauxErreur non null mais <= 10")
    void testStatutActifTauxErreurFaible() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTauxErreur(5.0);
      w.setVisible(true);
      assertThat(w.getStatutWidget()).isEqualTo("actif");
    }

    @Test
    @DisplayName("getStatutWidget retourne actif quand tauxErreur explicitement null")
    void testStatutActifTauxErreurNull() {
      DashboardWidgetResponse w = minimalWidget();
      w.setTauxErreur(15.0);
      w.setTauxErreur(null);
      assertThat(w.getStatutWidget()).isEqualTo("actif");
    }
  }
}
