package dev.lions.services;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;

import java.util.HashMap;
import lombok.extern.slf4j.Slf4j;

import dev.lions.models.Notification;
import dev.lions.models.NotificationStatus;
import dev.lions.models.NotificationType;
import dev.lions.repositories.NotificationRepository;
import dev.lions.dtos.NotificationDTO;
import dev.lions.exceptions.NotificationException;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Service gérant les notifications système de l'application.
 * Cette classe assure la création, l'envoi et le suivi des notifications
 * avec support pour différents types de notifications et canaux de distribution.
 */
@Slf4j
@ApplicationScoped
public class NotificationService {

  @Inject
  NotificationRepository notificationRepository;

  @Inject
  WebSocketService webSocketService;

  @Inject
  EmailService emailService;

  private static final int MAX_BATCH_SIZE = 100;

  /**
   * Crée et envoie une nouvelle notification interne.
   *
   * @param type Type de notification
   * @param message Contenu de la notification
   * @return Notification créée
   */
  @Transactional
  public Notification sendInternalNotification(
      @NotNull NotificationType type,
      @NotNull String message) {

    log.info("Création d'une notification interne de type : {}", type);

    try {
      Notification notification = createNotification(type, message);
      notification = notificationRepository.save(notification);

      distributeNotification(notification);

      log.info("Notification créée et distribuée avec succès - ID: {}",
               notification.getId());
      return notification;

    } catch (Exception e) {
      log.error("Erreur lors de l'envoi de la notification interne", e);
      throw new NotificationException(
          "Impossible d'envoyer la notification interne", e);
    }
  }

  /**
   * Crée une nouvelle notification.
   */
  private Notification createNotification(NotificationType type, String message) {
    return Notification.builder()
                       .title(generateTitle(type))
                       .message(message)
                       .type(type)
                       .status(NotificationStatus.UNREAD)
                       .timestamp(LocalDateTime.now())
                       .data(createNotificationData())
                       .build();
  }

  /**
   * Distribue la notification via différents canaux.
   */
  private void distributeNotification(Notification notification) {
    // Envoi WebSocket pour mise à jour en temps réel
    webSocketService.broadcastToAdmins(NotificationDTO.from(notification));

    // Envoi d'email pour les notifications critiques
    if (notification.isCritical()) {
      emailService.sendNotificationEmail(notification);
    }
  }

  /**
   * Génère un titre approprié selon le type de notification.
   */
  private String generateTitle(NotificationType type) {
    return switch (type) {
      case NEW_CONTACT -> "Nouveau message de contact";
      case SYSTEM_ALERT -> "Alerte système";
      case SECURITY_ALERT -> "Alerte de sécurité";
      default -> "Notification";
    };
  }

  /**
   * Crée les données additionnelles de la notification.
   */
  private Notification.NotificationData createNotificationData() {
    return Notification.NotificationData.builder()
                                        .attributes(new HashMap<>())
                                        .metadata(new HashMap<>())
                                        .build();
  }

  /**
   * Récupère les notifications non lues.
   */
  public List<Notification> getUnreadNotifications() {
    log.debug("Récupération des notifications non lues");
    return notificationRepository.findUnreadNotifications();
  }

  /**
   * Marque une notification comme lue.
   */
  @Transactional
  public void markAsRead(@NotNull Long notificationId) {
    log.debug("Marquage de la notification {} comme lue", notificationId);
    notificationRepository.markAsRead(notificationId);
  }

  /**
   * Récupère les notifications critiques actives.
   */
  public List<Notification> getCriticalNotifications() {
    log.debug("Récupération des notifications critiques");
    return notificationRepository.findCriticalNotifications();
  }

  /**
   * Nettoie les anciennes notifications.
   *
   * @param retentionDays Nombre de jours de rétention
   * @return Nombre de notifications supprimées
   */
  @Transactional
  public int cleanupOldNotifications(int retentionDays) {
    log.info("Nettoyage des notifications plus anciennes que {} jours",
             retentionDays);

    LocalDateTime retentionDate = LocalDateTime.now()
                                               .minusDays(retentionDays);

    try {
      int deletedCount = notificationRepository
          .deleteEventsOlderThan(retentionDate);

      log.info("{} notifications anciennes supprimées", deletedCount);
      return deletedCount;

    } catch (Exception e) {
      log.error("Erreur lors du nettoyage des notifications", e);
      throw new NotificationException(
          "Impossible de nettoyer les anciennes notifications", e);
    }
  }

  /**
   * Récupère les statistiques des notifications.
   */
  public Map<NotificationType, Long> getNotificationStatistics(
      LocalDateTime startDate,
      LocalDateTime endDate) {

    log.debug("Calcul des statistiques de notifications entre {} et {}",
              startDate, endDate);

    return notificationRepository.countByType(startDate, endDate);
  }
}