package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.notification.NotificationDTO;
import dev.lions.unionflow.server.api.dto.notification.PreferencesNotificationDTO;
import dev.lions.unionflow.server.api.enums.notification.StatutNotification;
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;

/**
 * Service principal de gestion des notifications UnionFlow
 *
 * <p>Ce service orchestre l'envoi, la gestion et le suivi des notifications avec intégration
 * Firebase, templates dynamiques et préférences utilisateur.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@ApplicationScoped
public class NotificationService {

  private static final Logger LOG = Logger.getLogger(NotificationService.class);

  // @Inject
  // FirebaseNotificationService firebaseService;

  // @Inject
  // NotificationTemplateService templateService;

  // @Inject
  // PreferencesNotificationService preferencesService;

  // @Inject
  // NotificationHistoryService historyService;

  // @Inject
  // NotificationSchedulerService schedulerService;

  @ConfigProperty(name = "unionflow.notifications.enabled", defaultValue = "true")
  boolean notificationsEnabled;

  @ConfigProperty(name = "unionflow.notifications.batch-size", defaultValue = "100")
  int batchSize;

  @ConfigProperty(name = "unionflow.notifications.retry-attempts", defaultValue = "3")
  int maxRetryAttempts;

  @ConfigProperty(name = "unionflow.notifications.retry-delay-minutes", defaultValue = "5")
  int retryDelayMinutes;

  // Cache des préférences utilisateur pour optimiser les performances
  private final Map<String, PreferencesNotificationDTO> preferencesCache =
      new ConcurrentHashMap<>();

  // Statistiques en temps réel
  private final Map<String, Long> statistiques = new ConcurrentHashMap<>();

  /**
   * Envoie une notification simple
   *
   * @param notification La notification à envoyer
   * @return CompletableFuture avec le résultat de l'envoi
   */
  public CompletableFuture<NotificationDTO> envoyerNotification(NotificationDTO notification) {
    LOG.infof("Envoi de notification: %s", notification.getId());

    return CompletableFuture.supplyAsync(
        () -> {
          try {
            // Validation des données
            validerNotification(notification);

            // Vérification des préférences utilisateur
            if (!verifierPreferencesUtilisateur(notification)) {
              notification.setStatut(StatutNotification.ANNULEE);
              notification.setMessageErreur("Notification bloquée par les préférences utilisateur");
              return notification;
            }

            // Application des templates
            // notification = templateService.appliquerTemplate(notification);

            // Envoi via Firebase
            notification.setStatut(StatutNotification.EN_COURS_ENVOI);
            notification.setDateEnvoi(LocalDateTime.now());

            // Envoi via Firebase (à implémenter quand Firebase sera configuré)
            boolean succes = false;
            try {
                // boolean succes = firebaseService.envoyerNotificationPush(notification);
                // Pour l'instant, on considère que l'envoi est réussi si la notification est créée
                succes = true;
            } catch (Exception e) {
                LOG.errorf(e, "Erreur lors de l'envoi de la notification via Firebase");
                succes = false;
            }

            if (succes) {
              notification.setStatut(StatutNotification.ENVOYEE);
              incrementerStatistique("notifications_envoyees");
            } else {
              notification.setStatut(StatutNotification.ECHEC_ENVOI);
              incrementerStatistique("notifications_echec");
            }

            // Sauvegarde dans l'historique
            // historyService.sauvegarderNotification(notification);

            return notification;

          } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de l'envoi de la notification %s", notification.getId());
            notification.setStatut(StatutNotification.ERREUR_TECHNIQUE);
            notification.setMessageErreur(e.getMessage());
            notification.setTraceErreur(Arrays.toString(e.getStackTrace()));
            incrementerStatistique("notifications_erreur");
            return notification;
          }
        });
  }

  /**
   * Envoie une notification à plusieurs destinataires
   *
   * @param typeNotification Type de notification
   * @param titre Titre de la notification
   * @param message Message de la notification
   * @param destinatairesIds Liste des IDs des destinataires
   * @param donneesPersonnalisees Données personnalisées
   * @return CompletableFuture avec la liste des résultats
   */
  public CompletableFuture<List<NotificationDTO>> envoyerNotificationGroupe(
      TypeNotification typeNotification,
      String titre,
      String message,
      List<String> destinatairesIds,
      Map<String, Object> donneesPersonnalisees) {

    LOG.infof("Envoi de notification de groupe: %s destinataires", destinatairesIds.size());

    return CompletableFuture.supplyAsync(
        () -> {
          List<NotificationDTO> resultats = new ArrayList<>();

          // Traitement par batch pour optimiser les performances
          for (int i = 0; i < destinatairesIds.size(); i += batchSize) {
            int fin = Math.min(i + batchSize, destinatairesIds.size());
            List<String> batch = destinatairesIds.subList(i, fin);

            List<CompletableFuture<NotificationDTO>> futures =
                batch.stream()
                    .map(
                        destinataireId -> {
                          NotificationDTO notification =
                              new NotificationDTO(
                                  typeNotification, titre, message, List.of(destinataireId));
                          notification.setId(UUID.randomUUID().toString());
                          notification.setDonneesPersonnalisees(donneesPersonnalisees);

                          return envoyerNotification(notification);
                        })
                    .toList();

            // Attendre que tous les envois du batch soient terminés
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            // Collecter les résultats
            futures.forEach(
                future -> {
                  try {
                    resultats.add(future.get());
                  } catch (Exception e) {
                    LOG.errorf(e, "Erreur lors de la récupération du résultat");
                  }
                });
          }

          incrementerStatistique("notifications_groupe_envoyees");
          return resultats;
        });
  }

  /**
   * Programme une notification pour envoi ultérieur
   *
   * @param notification La notification à programmer
   * @param dateEnvoi Date et heure d'envoi programmé
   * @return La notification programmée
   */
  @Transactional
  public NotificationDTO programmerNotification(
      NotificationDTO notification, LocalDateTime dateEnvoi) {
    LOG.infof("Programmation de notification pour: %s", dateEnvoi);

    notification.setId(UUID.randomUUID().toString());
    notification.setStatut(StatutNotification.PROGRAMMEE);
    notification.setDateEnvoiProgramme(dateEnvoi);
    notification.setDateCreation(LocalDateTime.now());

    // Validation
    validerNotification(notification);

    // Sauvegarde
    // historyService.sauvegarderNotification(notification);

    // Programmation dans le scheduler
    // schedulerService.programmerNotification(notification);

    incrementerStatistique("notifications_programmees");
    return notification;
  }

  /**
   * Annule une notification programmée
   *
   * @param notificationId ID de la notification à annuler
   * @return true si l'annulation a réussi
   */
  @Transactional
  public boolean annulerNotificationProgrammee(String notificationId) {
    LOG.infof("Annulation de notification programmée: %s", notificationId);

    try {
      // À implémenter quand les services seront configurés
      // NotificationDTO notification = historyService.obtenirNotification(notificationId);
      // if (notification != null && notification.getStatut().permetAnnulation()) {
      //     notification.setStatut(StatutNotification.ANNULEE);
      //     historyService.mettreAJourNotification(notification);
      //     schedulerService.annulerNotificationProgrammee(notificationId);
      //     incrementerStatistique("notifications_annulees");
      //     return true;
      // }
      
      incrementerStatistique("notifications_annulees");
      return true;

    } catch (Exception e) {
      LOG.errorf(e, "Erreur lors de l'annulation de la notification %s", notificationId);
      return false;
    }
  }

  /**
   * Marque une notification comme lue
   *
   * @param notificationId ID de la notification
   * @param utilisateurId ID de l'utilisateur
   * @return true si le marquage a réussi
   */
  @Transactional
  public boolean marquerCommeLue(String notificationId, String utilisateurId) {
    LOG.debugf(
        "Marquage comme lue: notification=%s, utilisateur=%s", notificationId, utilisateurId);

    try {
      // À implémenter quand les services seront configurés
      // NotificationDTO notification = historyService.obtenirNotification(notificationId);
      // if (notification != null && notification.getDestinatairesIds().contains(utilisateurId)) {
      //     notification.setEstLue(true);
      //     notification.setDateDerniereLecture(LocalDateTime.now());
      //     notification.setStatut(StatutNotification.LUE);
      //     historyService.mettreAJourNotification(notification);
      //     incrementerStatistique("notifications_lues");
      //     return true;
      // }

      incrementerStatistique("notifications_lues");
      return true;

    } catch (Exception e) {
      LOG.errorf(e, "Erreur lors du marquage comme lue: %s", notificationId);
      return false;
    }
  }

  /**
   * Archive une notification
   *
   * @param notificationId ID de la notification
   * @param utilisateurId ID de l'utilisateur
   * @return true si l'archivage a réussi
   */
  @Transactional
  public boolean archiverNotification(String notificationId, String utilisateurId) {
    LOG.debugf("Archivage: notification=%s, utilisateur=%s", notificationId, utilisateurId);

    try {
      // À implémenter quand les services seront configurés
      // NotificationDTO notification = historyService.obtenirNotification(notificationId);
      // if (notification != null && notification.getDestinatairesIds().contains(utilisateurId)) {
      //     notification.setEstArchivee(true);
      //     notification.setStatut(StatutNotification.ARCHIVEE);
      //     historyService.mettreAJourNotification(notification);
      //     incrementerStatistique("notifications_archivees");
      //     return true;
      // }
      
      incrementerStatistique("notifications_archivees");
      return true;

    } catch (Exception e) {
      LOG.errorf(e, "Erreur lors de l'archivage: %s", notificationId);
      return false;
    }
  }

  /**
   * Obtient les notifications d'un utilisateur
   *
   * @param utilisateurId ID de l'utilisateur
   * @param includeArchivees Inclure les notifications archivées
   * @param limite Nombre maximum de notifications à retourner
   * @return Liste des notifications
   */
  public List<NotificationDTO> obtenirNotificationsUtilisateur(
      String utilisateurId, boolean includeArchivees, int limite) {

    LOG.debugf("Récupération notifications utilisateur: %s", utilisateurId);

    try {
      // À implémenter quand les services seront configurés
      // return historyService.obtenirNotificationsUtilisateur(
      //     utilisateurId, includeArchivees, limite
      // );
      
      return new ArrayList<>();
    } catch (Exception e) {
      LOG.errorf(e, "Erreur lors de la récupération des notifications pour %s", utilisateurId);
      return new ArrayList<>();
    }
  }

  /**
   * Obtient les statistiques des notifications
   *
   * @return Map des statistiques
   */
  public Map<String, Long> obtenirStatistiques() {
    Map<String, Long> stats = new HashMap<>(statistiques);

    // Ajout des statistiques calculées
    stats.put(
        "notifications_total",
        stats.getOrDefault("notifications_envoyees", 0L)
            + stats.getOrDefault("notifications_echec", 0L)
            + stats.getOrDefault("notifications_erreur", 0L));

    long envoyees = stats.getOrDefault("notifications_envoyees", 0L);
    long total = stats.get("notifications_total");

    if (total > 0) {
      stats.put("taux_succes_pct", (envoyees * 100) / total);
    }

    return stats;
  }

  /**
   * Envoie une notification de test
   *
   * @param utilisateurId ID de l'utilisateur
   * @param typeNotification Type de notification à tester
   * @return La notification de test envoyée
   */
  public CompletableFuture<NotificationDTO> envoyerNotificationTest(
      String utilisateurId, TypeNotification typeNotification) {

    LOG.infof(
        "Envoi notification de test: utilisateur=%s, type=%s", utilisateurId, typeNotification);

    NotificationDTO notification =
        new NotificationDTO(
            typeNotification,
            "Test - " + typeNotification.getLibelle(),
            "Ceci est une notification de test pour vérifier vos paramètres.",
            List.of(utilisateurId));

    notification.setId("test-" + UUID.randomUUID().toString());
    notification.getDonneesPersonnalisees().put("test", true);
    notification.getTags().add("test");

    return envoyerNotification(notification);
  }

  // === MÉTHODES PRIVÉES ===

  /** Valide une notification avant envoi */
  private void validerNotification(NotificationDTO notification) {
    if (notification.getTitre() == null || notification.getTitre().trim().isEmpty()) {
      throw new IllegalArgumentException("Le titre de la notification est obligatoire");
    }

    if (notification.getMessage() == null || notification.getMessage().trim().isEmpty()) {
      throw new IllegalArgumentException("Le message de la notification est obligatoire");
    }

    if (notification.getDestinatairesIds() == null
        || notification.getDestinatairesIds().isEmpty()) {
      throw new IllegalArgumentException("Au moins un destinataire est requis");
    }

    if (notification.getTypeNotification() == null) {
      throw new IllegalArgumentException("Le type de notification est obligatoire");
    }
  }

  /** Vérifie les préférences utilisateur pour une notification */
  private boolean verifierPreferencesUtilisateur(NotificationDTO notification) {
    if (!notificationsEnabled) {
      return false;
    }

    // Vérification pour chaque destinataire
    for (String destinataireId : notification.getDestinatairesIds()) {
      PreferencesNotificationDTO preferences = obtenirPreferencesUtilisateur(destinataireId);

      if (preferences == null || !preferences.getNotificationsActivees()) {
        return false;
      }

      if (!preferences.isTypeActive(notification.getTypeNotification())) {
        return false;
      }

      if (!preferences.isCanalActif(notification.getCanal())) {
        return false;
      }

      if (preferences.isExpediteurBloque(notification.getExpediteurId())) {
        return false;
      }

      if (preferences.isEnModeSilencieux()
          && !notification.getTypeNotification().isCritique()
          && !preferences.getUrgentesIgnorentSilencieux()) {
        return false;
      }
    }

    return true;
  }

  /** Obtient les préférences d'un utilisateur (avec cache) */
  private PreferencesNotificationDTO obtenirPreferencesUtilisateur(String utilisateurId) {
    return preferencesCache.computeIfAbsent(
        utilisateurId,
        id -> {
          try {
            // Note: Les préférences sont actuellement initialisées avec des valeurs par défaut.
            // L'intégration avec le service de préférences sera implémentée ultérieurement.
            // return preferencesService.obtenirPreferences(id);
            return new PreferencesNotificationDTO(id);
          } catch (Exception e) {
            LOG.warnf(
                "Impossible de récupérer les préférences pour %s, utilisation des défauts", id);
            return new PreferencesNotificationDTO(id);
          }
        });
  }

  /** Incrémente une statistique */
  private void incrementerStatistique(String cle) {
    statistiques.merge(cle, 1L, Long::sum);
  }

  /** Vide le cache des préférences */
  public void viderCachePreferences() {
    preferencesCache.clear();
    LOG.info("Cache des préférences vidé");
  }

  /** Recharge les préférences d'un utilisateur */
  public void rechargerPreferencesUtilisateur(String utilisateurId) {
    preferencesCache.remove(utilisateurId);
    LOG.debugf("Préférences rechargées pour l'utilisateur: %s", utilisateurId);
  }
}
