package dev.lions.unionflow.server.service;

import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.jboss.logging.Logger;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Service pour programmer et gérer les notifications différées
 */
@ApplicationScoped
public class NotificationSchedulerService {

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

    @Inject
    NotificationService notificationService;

    @Inject
    NotificationHistoryService notificationHistoryService;

    // Stockage temporaire des notifications programmées
    private final Map<UUID, ScheduledNotification> notificationsProgrammees = new ConcurrentHashMap<>();

    /**
     * Programme une notification pour un envoi différé
     */
    public UUID programmerNotification(UUID utilisateurId, String type, String titre, String message,
                                     LocalDateTime dateEnvoi, String canal) {
        LOG.infof("Programmation d'une notification %s pour l'utilisateur %s à %s", type, utilisateurId, dateEnvoi);
        
        UUID notificationId = UUID.randomUUID();
        
        ScheduledNotification notification = ScheduledNotification.builder()
                .id(notificationId)
                .utilisateurId(utilisateurId)
                .type(type)
                .titre(titre)
                .message(message)
                .dateEnvoi(dateEnvoi)
                .canal(canal)
                .statut("PROGRAMMEE")
                .dateProgrammation(LocalDateTime.now())
                .build();

        notificationsProgrammees.put(notificationId, notification);
        
        return notificationId;
    }

    /**
     * Programme une notification récurrente
     */
    public UUID programmerNotificationRecurrente(UUID utilisateurId, String type, String titre, String message,
                                               LocalDateTime premierEnvoi, String frequence, String canal) {
        LOG.infof("Programmation d'une notification récurrente %s pour l'utilisateur %s", type, utilisateurId);
        
        UUID notificationId = UUID.randomUUID();
        
        ScheduledNotification notification = ScheduledNotification.builder()
                .id(notificationId)
                .utilisateurId(utilisateurId)
                .type(type)
                .titre(titre)
                .message(message)
                .dateEnvoi(premierEnvoi)
                .canal(canal)
                .statut("PROGRAMMEE")
                .dateProgrammation(LocalDateTime.now())
                .recurrente(true)
                .frequence(frequence)
                .build();

        notificationsProgrammees.put(notificationId, notification);
        
        return notificationId;
    }

    /**
     * Annule une notification programmée
     */
    public boolean annulerNotification(UUID notificationId) {
        LOG.infof("Annulation de la notification programmée %s", notificationId);
        
        ScheduledNotification notification = notificationsProgrammees.get(notificationId);
        if (notification != null && "PROGRAMMEE".equals(notification.getStatut())) {
            notification.setStatut("ANNULEE");
            return true;
        }
        
        return false;
    }

    /**
     * Obtient toutes les notifications programmées pour un utilisateur
     */
    public List<ScheduledNotification> obtenirNotificationsProgrammees(UUID utilisateurId) {
        return notificationsProgrammees.values().stream()
                .filter(notification -> notification.getUtilisateurId().equals(utilisateurId))
                .filter(notification -> "PROGRAMMEE".equals(notification.getStatut()))
                .sorted(Comparator.comparing(ScheduledNotification::getDateEnvoi))
                .toList();
    }

    /**
     * Traite les notifications programmées (exécuté toutes les minutes)
     */
    @Scheduled(every = "1m")
    public void traiterNotificationsProgrammees() {
        LOG.debug("Traitement des notifications programmées");
        
        LocalDateTime maintenant = LocalDateTime.now();
        
        List<ScheduledNotification> aEnvoyer = notificationsProgrammees.values().stream()
                .filter(notification -> "PROGRAMMEE".equals(notification.getStatut()))
                .filter(notification -> notification.getDateEnvoi().isBefore(maintenant) || 
                                      notification.getDateEnvoi().isEqual(maintenant))
                .toList();

        for (ScheduledNotification notification : aEnvoyer) {
            try {
                envoyerNotificationProgrammee(notification);
                
                if (notification.isRecurrente()) {
                    programmerProchainEnvoi(notification);
                } else {
                    notification.setStatut("ENVOYEE");
                }
                
            } catch (Exception e) {
                LOG.errorf(e, "Erreur lors de l'envoi de la notification programmée %s", notification.getId());
                notification.setStatut("ERREUR");
                notification.setMessageErreur(e.getMessage());
            }
        }
    }

    /**
     * Envoie une notification programmée
     */
    private void envoyerNotificationProgrammee(ScheduledNotification notification) {
        LOG.infof("Envoi de la notification programmée %s", notification.getId());
        
        // Utiliser le service de notification approprié selon le canal
        switch (notification.getCanal().toUpperCase()) {
            case "PUSH":
                // Envoyer notification push
                break;
            case "EMAIL":
                // Envoyer email
                break;
            case "SMS":
                // Envoyer SMS
                break;
            default:
                LOG.warnf("Canal de notification non supporté: %s", notification.getCanal());
        }
        
        // Enregistrer dans l'historique
        notificationHistoryService.enregistrerNotification(
                notification.getUtilisateurId(),
                notification.getType(),
                notification.getTitre(),
                notification.getMessage(),
                notification.getCanal(),
                true
        );
    }

    /**
     * Programme le prochain envoi pour une notification récurrente
     */
    private void programmerProchainEnvoi(ScheduledNotification notification) {
        LocalDateTime prochainEnvoi = calculerProchainEnvoi(notification.getDateEnvoi(), notification.getFrequence());
        notification.setDateEnvoi(prochainEnvoi);
        
        LOG.infof("Prochaine occurrence de la notification récurrente %s programmée pour %s", 
                notification.getId(), prochainEnvoi);
    }

    /**
     * Calcule la prochaine date d'envoi selon la fréquence
     */
    private LocalDateTime calculerProchainEnvoi(LocalDateTime dernierEnvoi, String frequence) {
        return switch (frequence.toUpperCase()) {
            case "QUOTIDIEN" -> dernierEnvoi.plusDays(1);
            case "HEBDOMADAIRE" -> dernierEnvoi.plusWeeks(1);
            case "MENSUEL" -> dernierEnvoi.plusMonths(1);
            case "ANNUEL" -> dernierEnvoi.plusYears(1);
            default -> dernierEnvoi.plusDays(1);
        };
    }

    /**
     * Nettoie les notifications anciennes (exécuté quotidiennement)
     */
    @Scheduled(cron = "0 0 2 * * ?") // Tous les jours à 2h du matin
    public void nettoyerNotificationsAnciennes() {
        LOG.info("Nettoyage des notifications anciennes");
        
        LocalDateTime dateLimit = LocalDateTime.now().minusDays(30);
        
        List<UUID> aSupprimer = notificationsProgrammees.values().stream()
                .filter(notification -> "ENVOYEE".equals(notification.getStatut()) || 
                                      "ANNULEE".equals(notification.getStatut()) || 
                                      "ERREUR".equals(notification.getStatut()))
                .filter(notification -> notification.getDateProgrammation().isBefore(dateLimit))
                .map(ScheduledNotification::getId)
                .toList();

        aSupprimer.forEach(notificationsProgrammees::remove);
        
        LOG.infof("Suppression de %d notifications anciennes", aSupprimer.size());
    }

    /**
     * Classe interne pour représenter une notification programmée
     */
    public static class ScheduledNotification {
        private UUID id;
        private UUID utilisateurId;
        private String type;
        private String titre;
        private String message;
        private LocalDateTime dateEnvoi;
        private String canal;
        private String statut;
        private LocalDateTime dateProgrammation;
        private boolean recurrente;
        private String frequence;
        private String messageErreur;

        // Constructeurs
        public ScheduledNotification() {}

        private ScheduledNotification(Builder builder) {
            this.id = builder.id;
            this.utilisateurId = builder.utilisateurId;
            this.type = builder.type;
            this.titre = builder.titre;
            this.message = builder.message;
            this.dateEnvoi = builder.dateEnvoi;
            this.canal = builder.canal;
            this.statut = builder.statut;
            this.dateProgrammation = builder.dateProgrammation;
            this.recurrente = builder.recurrente;
            this.frequence = builder.frequence;
        }

        public static Builder builder() {
            return new Builder();
        }

        // Getters et Setters
        public UUID getId() { return id; }
        public void setId(UUID id) { this.id = id; }

        public UUID getUtilisateurId() { return utilisateurId; }
        public void setUtilisateurId(UUID utilisateurId) { this.utilisateurId = utilisateurId; }

        public String getType() { return type; }
        public void setType(String type) { this.type = type; }

        public String getTitre() { return titre; }
        public void setTitre(String titre) { this.titre = titre; }

        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }

        public LocalDateTime getDateEnvoi() { return dateEnvoi; }
        public void setDateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; }

        public String getCanal() { return canal; }
        public void setCanal(String canal) { this.canal = canal; }

        public String getStatut() { return statut; }
        public void setStatut(String statut) { this.statut = statut; }

        public LocalDateTime getDateProgrammation() { return dateProgrammation; }
        public void setDateProgrammation(LocalDateTime dateProgrammation) { this.dateProgrammation = dateProgrammation; }

        public boolean isRecurrente() { return recurrente; }
        public void setRecurrente(boolean recurrente) { this.recurrente = recurrente; }

        public String getFrequence() { return frequence; }
        public void setFrequence(String frequence) { this.frequence = frequence; }

        public String getMessageErreur() { return messageErreur; }
        public void setMessageErreur(String messageErreur) { this.messageErreur = messageErreur; }

        // Builder
        public static class Builder {
            private UUID id;
            private UUID utilisateurId;
            private String type;
            private String titre;
            private String message;
            private LocalDateTime dateEnvoi;
            private String canal;
            private String statut;
            private LocalDateTime dateProgrammation;
            private boolean recurrente;
            private String frequence;

            public Builder id(UUID id) { this.id = id; return this; }
            public Builder utilisateurId(UUID utilisateurId) { this.utilisateurId = utilisateurId; return this; }
            public Builder type(String type) { this.type = type; return this; }
            public Builder titre(String titre) { this.titre = titre; return this; }
            public Builder message(String message) { this.message = message; return this; }
            public Builder dateEnvoi(LocalDateTime dateEnvoi) { this.dateEnvoi = dateEnvoi; return this; }
            public Builder canal(String canal) { this.canal = canal; return this; }
            public Builder statut(String statut) { this.statut = statut; return this; }
            public Builder dateProgrammation(LocalDateTime dateProgrammation) { this.dateProgrammation = dateProgrammation; return this; }
            public Builder recurrente(boolean recurrente) { this.recurrente = recurrente; return this; }
            public Builder frequence(String frequence) { this.frequence = frequence; return this; }

            public ScheduledNotification build() {
                return new ScheduledNotification(this);
            }
        }
    }
}
