package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.notification.NotificationDTO;
import dev.lions.unionflow.server.api.dto.notification.TemplateNotificationDTO;
import dev.lions.unionflow.server.api.enums.notification.PrioriteNotification;
import dev.lions.unionflow.server.api.enums.notification.StatutNotification;
import dev.lions.unionflow.server.entity.*;
import dev.lions.unionflow.server.repository.*;
import dev.lions.unionflow.server.service.KeycloakService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.NotFoundException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;

/**
 * Service métier pour la gestion des notifications
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2025-01-29
 */
@ApplicationScoped
public class NotificationService {

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

  @Inject NotificationRepository notificationRepository;

  @Inject TemplateNotificationRepository templateNotificationRepository;

  @Inject MembreRepository membreRepository;

  @Inject OrganisationRepository organisationRepository;

  @Inject KeycloakService keycloakService;

  /**
   * Crée un nouveau template de notification
   *
   * @param templateDTO DTO du template à créer
   * @return DTO du template créé
   */
  @Transactional
  public TemplateNotificationDTO creerTemplate(TemplateNotificationDTO templateDTO) {
    LOG.infof("Création d'un nouveau template: %s", templateDTO.getCode());

    // Vérifier l'unicité du code
    if (templateNotificationRepository.findByCode(templateDTO.getCode()).isPresent()) {
      throw new IllegalArgumentException("Un template avec ce code existe déjà: " + templateDTO.getCode());
    }

    TemplateNotification template = convertToEntity(templateDTO);
    template.setCreePar(keycloakService.getCurrentUserEmail());

    templateNotificationRepository.persist(template);
    LOG.infof("Template créé avec succès: ID=%s, Code=%s", template.getId(), template.getCode());

    return convertToDTO(template);
  }

  /**
   * Crée une nouvelle notification
   *
   * @param notificationDTO DTO de la notification à créer
   * @return DTO de la notification créée
   */
  @Transactional
  public NotificationDTO creerNotification(NotificationDTO notificationDTO) {
    LOG.infof("Création d'une nouvelle notification: %s", notificationDTO.getTypeNotification());

    Notification notification = convertToEntity(notificationDTO);
    notification.setCreePar(keycloakService.getCurrentUserEmail());

    notificationRepository.persist(notification);
    LOG.infof("Notification créée avec succès: ID=%s", notification.getId());

    return convertToDTO(notification);
  }

  /**
   * Marque une notification comme lue
   *
   * @param id ID de la notification
   * @return DTO de la notification mise à jour
   */
  @Transactional
  public NotificationDTO marquerCommeLue(UUID id) {
    LOG.infof("Marquage de la notification comme lue: ID=%s", id);

    Notification notification =
        notificationRepository
            .findByIdOptional(id)
            .orElseThrow(() -> new NotFoundException("Notification non trouvée avec l'ID: " + id));

    notification.setStatut(StatutNotification.LUE);
    notification.setDateLecture(LocalDateTime.now());
    notification.setModifiePar(keycloakService.getCurrentUserEmail());

    notificationRepository.persist(notification);
    LOG.infof("Notification marquée comme lue: ID=%s", id);

    return convertToDTO(notification);
  }

  /**
   * Trouve une notification par son ID
   *
   * @param id ID de la notification
   * @return DTO de la notification
   */
  public NotificationDTO trouverNotificationParId(UUID id) {
    return notificationRepository
        .findByIdOptional(id)
        .map(this::convertToDTO)
        .orElseThrow(() -> new NotFoundException("Notification non trouvée avec l'ID: " + id));
  }

  /**
   * Liste toutes les notifications d'un membre
   *
   * @param membreId ID du membre
   * @return Liste des notifications
   */
  public List<NotificationDTO> listerNotificationsParMembre(UUID membreId) {
    return notificationRepository.findByMembreId(membreId).stream()
        .map(this::convertToDTO)
        .collect(Collectors.toList());
  }

  /**
   * Liste les notifications non lues d'un membre
   *
   * @param membreId ID du membre
   * @return Liste des notifications non lues
   */
  public List<NotificationDTO> listerNotificationsNonLuesParMembre(UUID membreId) {
    return notificationRepository.findNonLuesByMembreId(membreId).stream()
        .map(this::convertToDTO)
        .collect(Collectors.toList());
  }

  /**
   * Liste les notifications en attente d'envoi
   *
   * @return Liste des notifications en attente
   */
  public List<NotificationDTO> listerNotificationsEnAttenteEnvoi() {
    return notificationRepository.findEnAttenteEnvoi().stream()
        .map(this::convertToDTO)
        .collect(Collectors.toList());
  }

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

  /** Convertit une entité TemplateNotification en DTO */
  private TemplateNotificationDTO convertToDTO(TemplateNotification template) {
    if (template == null) {
      return null;
    }

    TemplateNotificationDTO dto = new TemplateNotificationDTO();
    dto.setId(template.getId());
    dto.setCode(template.getCode());
    dto.setSujet(template.getSujet());
    dto.setCorpsTexte(template.getCorpsTexte());
    dto.setCorpsHtml(template.getCorpsHtml());
    dto.setVariablesDisponibles(template.getVariablesDisponibles());
    dto.setCanauxSupportes(template.getCanauxSupportes());
    dto.setLangue(template.getLangue());
    dto.setDescription(template.getDescription());
    dto.setDateCreation(template.getDateCreation());
    dto.setDateModification(template.getDateModification());
    dto.setActif(template.getActif());

    return dto;
  }

  /** Convertit un DTO en entité TemplateNotification */
  private TemplateNotification convertToEntity(TemplateNotificationDTO dto) {
    if (dto == null) {
      return null;
    }

    TemplateNotification template = new TemplateNotification();
    template.setCode(dto.getCode());
    template.setSujet(dto.getSujet());
    template.setCorpsTexte(dto.getCorpsTexte());
    template.setCorpsHtml(dto.getCorpsHtml());
    template.setVariablesDisponibles(dto.getVariablesDisponibles());
    template.setCanauxSupportes(dto.getCanauxSupportes());
    template.setLangue(dto.getLangue() != null ? dto.getLangue() : "fr");
    template.setDescription(dto.getDescription());

    return template;
  }

  /** Convertit une entité Notification en DTO */
  private NotificationDTO convertToDTO(Notification notification) {
    if (notification == null) {
      return null;
    }

    NotificationDTO dto = new NotificationDTO();
    dto.setId(notification.getId());
    dto.setTypeNotification(notification.getTypeNotification());
    dto.setPriorite(notification.getPriorite());
    dto.setStatut(notification.getStatut());
    dto.setSujet(notification.getSujet());
    dto.setCorps(notification.getCorps());
    dto.setDateEnvoiPrevue(notification.getDateEnvoiPrevue());
    dto.setDateEnvoi(notification.getDateEnvoi());
    dto.setDateLecture(notification.getDateLecture());
    dto.setNombreTentatives(notification.getNombreTentatives());
    dto.setMessageErreur(notification.getMessageErreur());
    dto.setDonneesAdditionnelles(notification.getDonneesAdditionnelles());

    if (notification.getMembre() != null) {
      dto.setMembreId(notification.getMembre().getId());
    }
    if (notification.getOrganisation() != null) {
      dto.setOrganisationId(notification.getOrganisation().getId());
    }
    if (notification.getTemplate() != null) {
      dto.setTemplateId(notification.getTemplate().getId());
    }

    dto.setDateCreation(notification.getDateCreation());
    dto.setDateModification(notification.getDateModification());
    dto.setActif(notification.getActif());

    return dto;
  }

  /** Convertit un DTO en entité Notification */
  private Notification convertToEntity(NotificationDTO dto) {
    if (dto == null) {
      return null;
    }

    Notification notification = new Notification();
    notification.setTypeNotification(dto.getTypeNotification());
    notification.setPriorite(
        dto.getPriorite() != null ? dto.getPriorite() : PrioriteNotification.NORMALE);
    notification.setStatut(
        dto.getStatut() != null ? dto.getStatut() : StatutNotification.EN_ATTENTE);
    notification.setSujet(dto.getSujet());
    notification.setCorps(dto.getCorps());
    notification.setDateEnvoiPrevue(
        dto.getDateEnvoiPrevue() != null ? dto.getDateEnvoiPrevue() : LocalDateTime.now());
    notification.setDateEnvoi(dto.getDateEnvoi());
    notification.setDateLecture(dto.getDateLecture());
    notification.setNombreTentatives(dto.getNombreTentatives() != null ? dto.getNombreTentatives() : 0);
    notification.setMessageErreur(dto.getMessageErreur());
    notification.setDonneesAdditionnelles(dto.getDonneesAdditionnelles());

    // Relations
    if (dto.getMembreId() != null) {
      Membre membre =
          membreRepository
              .findByIdOptional(dto.getMembreId())
              .orElseThrow(
                  () -> new NotFoundException("Membre non trouvé avec l'ID: " + dto.getMembreId()));
      notification.setMembre(membre);
    }

    if (dto.getOrganisationId() != null) {
      Organisation org =
          organisationRepository
              .findByIdOptional(dto.getOrganisationId())
              .orElseThrow(
                  () ->
                      new NotFoundException(
                          "Organisation non trouvée avec l'ID: " + dto.getOrganisationId()));
      notification.setOrganisation(org);
    }

    if (dto.getTemplateId() != null) {
      TemplateNotification template =
          templateNotificationRepository
              .findByIdOptional(dto.getTemplateId())
              .orElseThrow(
                  () ->
                      new NotFoundException(
                          "Template non trouvé avec l'ID: " + dto.getTemplateId()));
      notification.setTemplate(template);
    }

    return notification;
  }
}
