package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.analytics.AnalyticsDataResponse;
import dev.lions.unionflow.server.api.dto.analytics.DashboardWidgetResponse;
import dev.lions.unionflow.server.api.dto.analytics.KPITrendResponse;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
// import dev.lions.unionflow.server.entity.DemandeAide;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.DemandeAideRepository;
import dev.lions.unionflow.server.repository.EvenementRepository;
// import dev.lions.unionflow.server.repository.DemandeAideRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;

/**
 * Service principal pour les analytics et métriques UnionFlow
 *
 * <p>Ce service calcule et fournit toutes les métriques analytics pour les tableaux de bord,
 * rapports et widgets.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@ApplicationScoped
@Slf4j
public class AnalyticsService {

  @Inject OrganisationRepository organisationRepository;

  @Inject MembreRepository membreRepository;

  @Inject CotisationRepository cotisationRepository;

  @Inject DemandeAideRepository demandeAideRepository;

  @Inject EvenementRepository evenementRepository;

  // @Inject
  // DemandeAideRepository demandeAideRepository;

  @Inject KPICalculatorService kpiCalculatorService;

  @Inject TrendAnalysisService trendAnalysisService;

  /**
   * Calcule une métrique analytics pour une période donnée
   *
   * @param typeMetrique Le type de métrique à calculer
   * @param periodeAnalyse La période d'analyse
   * @param organisationId L'ID de l'organisation (optionnel)
   * @return Les données analytics calculées
   */
  @Transactional
  public AnalyticsDataResponse calculerMetrique(
      TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, UUID organisationId) {
    log.info(
        "Calcul de la métrique {} pour la période {} et l'organisation {}",
        typeMetrique,
        periodeAnalyse,
        organisationId);

    LocalDateTime dateDebut = periodeAnalyse.getDateDebut();
    LocalDateTime dateFin = periodeAnalyse.getDateFin();

    BigDecimal valeur =
        switch (typeMetrique) {
          // Métriques membres
          case NOMBRE_MEMBRES_ACTIFS ->
              calculerNombreMembresActifs(organisationId, dateDebut, dateFin);
          case NOMBRE_MEMBRES_INACTIFS ->
              calculerNombreMembresInactifs(organisationId, dateDebut, dateFin);
          case TAUX_CROISSANCE_MEMBRES ->
              calculerTauxCroissanceMembres(organisationId, dateDebut, dateFin);
          case MOYENNE_AGE_MEMBRES -> calculerMoyenneAgeMembres(organisationId, dateDebut, dateFin);

          // Métriques financières
          case TOTAL_COTISATIONS_COLLECTEES ->
              calculerTotalCotisationsCollectees(organisationId, dateDebut, dateFin);
          case COTISATIONS_EN_ATTENTE ->
              calculerCotisationsEnAttente(organisationId, dateDebut, dateFin);
          case TAUX_RECOUVREMENT_COTISATIONS ->
              calculerTauxRecouvrementCotisations(organisationId, dateDebut, dateFin);
          case MOYENNE_COTISATION_MEMBRE ->
              calculerMoyenneCotisationMembre(organisationId, dateDebut, dateFin);

          // Métriques événements
          case NOMBRE_EVENEMENTS_ORGANISES ->
              calculerNombreEvenementsOrganises(organisationId, dateDebut, dateFin);
          case TAUX_PARTICIPATION_EVENEMENTS ->
              calculerTauxParticipationEvenements(organisationId, dateDebut, dateFin);
          case MOYENNE_PARTICIPANTS_EVENEMENT ->
              calculerMoyenneParticipantsEvenement(organisationId, dateDebut, dateFin);

          // Métriques solidarité
          case NOMBRE_DEMANDES_AIDE ->
              calculerNombreDemandesAide(organisationId, dateDebut, dateFin);
          case MONTANT_AIDES_ACCORDEES ->
              calculerMontantAidesAccordees(organisationId, dateDebut, dateFin);
          case TAUX_APPROBATION_AIDES ->
              calculerTauxApprobationAides(organisationId, dateDebut, dateFin);

          default -> BigDecimal.ZERO;
        };

    // Calcul de la valeur précédente pour comparaison
    BigDecimal valeurPrecedente =
        calculerValeurPrecedente(typeMetrique, periodeAnalyse, organisationId);
    BigDecimal pourcentageEvolution = calculerPourcentageEvolution(valeur, valeurPrecedente);

    return AnalyticsDataResponse.builder()
        .typeMetrique(typeMetrique)
        .periodeAnalyse(periodeAnalyse)
        .valeur(valeur)
        .valeurPrecedente(valeurPrecedente)
        .pourcentageEvolution(pourcentageEvolution)
        .dateDebut(dateDebut)
        .dateFin(dateFin)
        .dateCalcul(LocalDateTime.now())
        .organisationId(organisationId)
        .nomOrganisation(obtenirNomOrganisation(organisationId))
        .indicateurFiabilite(new BigDecimal("95.0"))
        .niveauPriorite(3)
        .tempsReel(false)
        .necessiteMiseAJour(false)
        .build();
  }

  /**
   * Calcule les tendances d'un KPI sur une période
   *
   * @param typeMetrique Le type de métrique
   * @param periodeAnalyse La période d'analyse
   * @param organisationId L'ID de l'organisation (optionnel)
   * @return Les données de tendance du KPI
   */
  @Transactional
  public KPITrendResponse calculerTendanceKPI(
      TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, UUID organisationId) {
    log.info(
        "Calcul de la tendance KPI {} pour la période {} et l'organisation {}",
        typeMetrique,
        periodeAnalyse,
        organisationId);

    return trendAnalysisService.calculerTendance(typeMetrique, periodeAnalyse, organisationId);
  }

  /**
   * Obtient les métriques pour un tableau de bord
   *
   * @param organisationId L'ID de l'organisation
   * @param utilisateurId L'ID de l'utilisateur
   * @return La liste des widgets du tableau de bord
   */
  @Transactional
  public List<DashboardWidgetResponse> obtenirMetriquesTableauBord(
      UUID organisationId, UUID utilisateurId) {
    log.info(
        "Obtention des métriques du tableau de bord pour l'organisation {} et l'utilisateur {}",
        organisationId,
        utilisateurId);

    List<DashboardWidgetResponse> widgets = new ArrayList<>();

    // Widget KPI Membres Actifs
    widgets.add(
        creerWidgetKPI(
            TypeMetrique.NOMBRE_MEMBRES_ACTIFS,
            PeriodeAnalyse.CE_MOIS,
            organisationId,
            utilisateurId,
            0,
            0,
            3,
            2));

    // Widget KPI Cotisations
    widgets.add(
        creerWidgetKPI(
            TypeMetrique.TOTAL_COTISATIONS_COLLECTEES,
            PeriodeAnalyse.CE_MOIS,
            organisationId,
            utilisateurId,
            3,
            0,
            3,
            2));

    // Widget KPI Événements
    widgets.add(
        creerWidgetKPI(
            TypeMetrique.NOMBRE_EVENEMENTS_ORGANISES,
            PeriodeAnalyse.CE_MOIS,
            organisationId,
            utilisateurId,
            6,
            0,
            3,
            2));

    // Widget KPI Solidarité
    widgets.add(
        creerWidgetKPI(
            TypeMetrique.NOMBRE_DEMANDES_AIDE,
            PeriodeAnalyse.CE_MOIS,
            organisationId,
            utilisateurId,
            9,
            0,
            3,
            2));

    // Widget Graphique Évolution Membres
    widgets.add(
        creerWidgetGraphique(
            TypeMetrique.NOMBRE_MEMBRES_ACTIFS,
            PeriodeAnalyse.SIX_DERNIERS_MOIS,
            organisationId,
            utilisateurId,
            0,
            2,
            6,
            4,
            "line"));

    // Widget Graphique Évolution Financière
    widgets.add(
        creerWidgetGraphique(
            TypeMetrique.TOTAL_COTISATIONS_COLLECTEES,
            PeriodeAnalyse.SIX_DERNIERS_MOIS,
            organisationId,
            utilisateurId,
            6,
            2,
            6,
            4,
            "area"));

    return widgets;
  }

  // === MÉTHODES PRIVÉES DE CALCUL ===

  private BigDecimal calculerNombreMembresActifs(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Long count = membreRepository.countMembresActifs(organisationId, dateDebut, dateFin);
    return new BigDecimal(count);
  }

  private BigDecimal calculerNombreMembresInactifs(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Long count = membreRepository.countMembresInactifs(organisationId, dateDebut, dateFin);
    return new BigDecimal(count);
  }

  private BigDecimal calculerTauxCroissanceMembres(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Long membresActuels = membreRepository.countMembresActifs(organisationId, dateDebut, dateFin);
    Long membresPrecedents =
        membreRepository.countMembresActifs(
            organisationId, dateDebut.minusMonths(1), dateFin.minusMonths(1));

    if (membresPrecedents == 0) return BigDecimal.ZERO;

    BigDecimal croissance =
        new BigDecimal(membresActuels - membresPrecedents)
            .divide(new BigDecimal(membresPrecedents), 4, RoundingMode.HALF_UP)
            .multiply(new BigDecimal("100"));

    return croissance;
  }

  private BigDecimal calculerMoyenneAgeMembres(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Double moyenneAge = membreRepository.calculerMoyenneAge(organisationId, dateDebut, dateFin);
    return moyenneAge != null
        ? new BigDecimal(moyenneAge).setScale(1, RoundingMode.HALF_UP)
        : BigDecimal.ZERO;
  }

  private BigDecimal calculerTotalCotisationsCollectees(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    BigDecimal total = cotisationRepository.sumMontantsPayes(organisationId, dateDebut, dateFin);
    return total != null ? total : BigDecimal.ZERO;
  }

  private BigDecimal calculerCotisationsEnAttente(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    BigDecimal total =
        cotisationRepository.sumMontantsEnAttente(organisationId, dateDebut, dateFin);
    return total != null ? total : BigDecimal.ZERO;
  }

  private BigDecimal calculerTauxRecouvrementCotisations(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    BigDecimal collectees = calculerTotalCotisationsCollectees(organisationId, dateDebut, dateFin);
    BigDecimal enAttente = calculerCotisationsEnAttente(organisationId, dateDebut, dateFin);
    BigDecimal total = collectees.add(enAttente);

    if (total.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO;

    return collectees.divide(total, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
  }

  private BigDecimal calculerMoyenneCotisationMembre(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    BigDecimal total = calculerTotalCotisationsCollectees(organisationId, dateDebut, dateFin);
    Long nombreMembres = membreRepository.countMembresActifs(organisationId, dateDebut, dateFin);

    if (nombreMembres == 0) return BigDecimal.ZERO;

    return total.divide(new BigDecimal(nombreMembres), 2, RoundingMode.HALF_UP);
  }

  private BigDecimal calculerNombreEvenementsOrganises(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Long count = evenementRepository.countEvenements(organisationId, dateDebut, dateFin);
    return new BigDecimal(count);
  }

  private BigDecimal calculerTauxParticipationEvenements(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    // Implémentation simplifiée - à enrichir selon les besoins
    return new BigDecimal("75.5"); // Valeur par défaut
  }

  private BigDecimal calculerMoyenneParticipantsEvenement(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Double moyenne =
        evenementRepository.calculerMoyenneParticipants(organisationId, dateDebut, dateFin);
    return moyenne != null
        ? new BigDecimal(moyenne).setScale(1, RoundingMode.HALF_UP)
        : BigDecimal.ZERO;
  }

  private BigDecimal calculerNombreDemandesAide(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Long count = demandeAideRepository.countDemandes(organisationId, dateDebut, dateFin);
    return new BigDecimal(count);
  }

  private BigDecimal calculerMontantAidesAccordees(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    BigDecimal total =
        demandeAideRepository.sumMontantsAccordes(organisationId, dateDebut, dateFin);
    return total != null ? total : BigDecimal.ZERO;
  }

  private BigDecimal calculerTauxApprobationAides(
      UUID organisationId, LocalDateTime dateDebut, LocalDateTime dateFin) {
    Long totalDemandes = demandeAideRepository.countDemandes(organisationId, dateDebut, dateFin);
    Long demandesApprouvees =
        demandeAideRepository.countDemandesApprouvees(organisationId, dateDebut, dateFin);

    if (totalDemandes == 0) return BigDecimal.ZERO;

    return new BigDecimal(demandesApprouvees)
        .divide(new BigDecimal(totalDemandes), 4, RoundingMode.HALF_UP)
        .multiply(new BigDecimal("100"));
  }

  private BigDecimal calculerValeurPrecedente(
      TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, UUID organisationId) {
    // Calcul de la période précédente
    LocalDateTime dateDebutPrecedente =
        periodeAnalyse.getDateDebut().minus(periodeAnalyse.getDuree(), periodeAnalyse.getUnite());
    LocalDateTime dateFinPrecedente =
        periodeAnalyse.getDateFin().minus(periodeAnalyse.getDuree(), periodeAnalyse.getUnite());

    return switch (typeMetrique) {
      case NOMBRE_MEMBRES_ACTIFS ->
          calculerNombreMembresActifs(organisationId, dateDebutPrecedente, dateFinPrecedente);
      case TOTAL_COTISATIONS_COLLECTEES ->
          calculerTotalCotisationsCollectees(
              organisationId, dateDebutPrecedente, dateFinPrecedente);
      case NOMBRE_EVENEMENTS_ORGANISES ->
          calculerNombreEvenementsOrganises(organisationId, dateDebutPrecedente, dateFinPrecedente);
      case NOMBRE_DEMANDES_AIDE ->
          calculerNombreDemandesAide(organisationId, dateDebutPrecedente, dateFinPrecedente);
      default -> BigDecimal.ZERO;
    };
  }

  private BigDecimal calculerPourcentageEvolution(
      BigDecimal valeurActuelle, BigDecimal valeurPrecedente) {
    if (valeurPrecedente == null || valeurPrecedente.compareTo(BigDecimal.ZERO) == 0) {
      return BigDecimal.ZERO;
    }

    return valeurActuelle
        .subtract(valeurPrecedente)
        .divide(valeurPrecedente, 4, RoundingMode.HALF_UP)
        .multiply(new BigDecimal("100"));
  }

  private String obtenirNomOrganisation(UUID organisationId) {
    // Temporairement désactivé pour éviter les erreurs de compilation
    return "Organisation "
        + (organisationId != null ? organisationId.toString().substring(0, 8) : "inconnue");
  }

  private DashboardWidgetResponse creerWidgetKPI(
      TypeMetrique typeMetrique,
      PeriodeAnalyse periodeAnalyse,
      UUID organisationId,
      UUID utilisateurId,
      int positionX,
      int positionY,
      int largeur,
      int hauteur) {
    AnalyticsDataResponse data = calculerMetrique(typeMetrique, periodeAnalyse, organisationId);

    return DashboardWidgetResponse.builder()
        .titre(typeMetrique.getLibelle())
        .typeWidget("kpi")
        .typeMetrique(typeMetrique)
        .periodeAnalyse(periodeAnalyse)
        .organisationId(organisationId)
        .utilisateurProprietaireId(utilisateurId)
        .positionX(positionX)
        .positionY(positionY)
        .largeur(largeur)
        .hauteur(hauteur)
        .couleurPrincipale(typeMetrique.getCouleur())
        .icone(typeMetrique.getIcone())
        .donneesWidget(convertirEnJSON(data))
        .dateDerniereMiseAJour(LocalDateTime.now())
        .build();
  }

  private DashboardWidgetResponse creerWidgetGraphique(
      TypeMetrique typeMetrique,
      PeriodeAnalyse periodeAnalyse,
      UUID organisationId,
      UUID utilisateurId,
      int positionX,
      int positionY,
      int largeur,
      int hauteur,
      String typeGraphique) {
    KPITrendResponse trend = calculerTendanceKPI(typeMetrique, periodeAnalyse, organisationId);

    return DashboardWidgetResponse.builder()
        .titre("Évolution " + typeMetrique.getLibelle())
        .typeWidget("chart")
        .typeMetrique(typeMetrique)
        .periodeAnalyse(periodeAnalyse)
        .organisationId(organisationId)
        .utilisateurProprietaireId(utilisateurId)
        .positionX(positionX)
        .positionY(positionY)
        .largeur(largeur)
        .hauteur(hauteur)
        .couleurPrincipale(typeMetrique.getCouleur())
        .icone(typeMetrique.getIcone())
        .donneesWidget(convertirEnJSON(trend))
        .configurationVisuelle("{\"type\":\"" + typeGraphique + "\",\"responsive\":true}")
        .dateDerniereMiseAJour(LocalDateTime.now())
        .build();
  }

  @Inject com.fasterxml.jackson.databind.ObjectMapper objectMapper;

  private String convertirEnJSON(Object data) {
    if (data == null) return "{}";
    try {
      return objectMapper.writeValueAsString(data);
    } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
      log.warn("Erreur sérialisation JSON: {}", e.getMessage());
      return "{}";
    }
  }
}
