package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.notification.NotificationDTO;
import dev.lions.unionflow.server.api.dto.notification.ActionNotificationDTO;
import dev.lions.unionflow.server.api.enums.notification.CanalNotification;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.annotation.PostConstruct;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;

import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.*;
import com.google.auth.oauth2.GoogleCredentials;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * Service Firebase pour l'envoi de notifications push
 * 
 * Ce service gère l'intégration avec Firebase Cloud Messaging (FCM)
 * pour l'envoi de notifications push vers les applications mobiles.
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@ApplicationScoped
public class FirebaseNotificationService {
    
    private static final Logger LOG = Logger.getLogger(FirebaseNotificationService.class);
    
    @ConfigProperty(name = "unionflow.firebase.credentials-path")
    Optional<String> firebaseCredentialsPath;
    
    @ConfigProperty(name = "unionflow.firebase.project-id")
    Optional<String> firebaseProjectId;
    
    @ConfigProperty(name = "unionflow.firebase.enabled", defaultValue = "true")
    boolean firebaseEnabled;
    
    @ConfigProperty(name = "unionflow.firebase.dry-run", defaultValue = "false")
    boolean dryRun;
    
    @ConfigProperty(name = "unionflow.firebase.batch-size", defaultValue = "500")
    int batchSize;
    
    private FirebaseMessaging firebaseMessaging;
    private boolean initialized = false;
    
    /**
     * Initialise Firebase
     */
    @PostConstruct
    public void init() {
        if (!firebaseEnabled) {
            LOG.info("Firebase désactivé par configuration");
            return;
        }
        
        try {
            if (firebaseCredentialsPath.isPresent() && firebaseProjectId.isPresent()) {
                GoogleCredentials credentials = GoogleCredentials
                    .fromStream(new FileInputStream(firebaseCredentialsPath.get()));
                
                FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(credentials)
                    .setProjectId(firebaseProjectId.get())
                    .build();
                
                if (FirebaseApp.getApps().isEmpty()) {
                    FirebaseApp.initializeApp(options);
                }
                
                firebaseMessaging = FirebaseMessaging.getInstance();
                initialized = true;
                
                LOG.infof("Firebase initialisé avec succès pour le projet: %s", firebaseProjectId.get());
            } else {
                LOG.warn("Configuration Firebase incomplète - credentials-path ou project-id manquant");
            }
        } catch (IOException e) {
            LOG.errorf(e, "Erreur lors de l'initialisation de Firebase");
        }
    }
    
    /**
     * Envoie une notification push à un seul destinataire
     * 
     * @param notification La notification à envoyer
     * @return true si l'envoi a réussi
     */
    public boolean envoyerNotificationPush(NotificationDTO notification) {
        if (!initialized || !firebaseEnabled) {
            LOG.warn("Firebase non initialisé ou désactivé");
            return false;
        }
        
        try {
            // Récupération du token FCM du destinataire
            String tokenFCM = obtenirTokenFCM(notification.getDestinatairesIds().get(0));
            
            if (tokenFCM == null || tokenFCM.isEmpty()) {
                LOG.warnf("Token FCM non trouvé pour le destinataire: %s", 
                         notification.getDestinatairesIds().get(0));
                return false;
            }
            
            // Construction du message Firebase
            Message message = construireMessage(notification, tokenFCM);
            
            // Envoi
            String response = firebaseMessaging.send(message, dryRun);
            
            LOG.infof("Notification envoyée avec succès: %s", response);
            return true;
            
        } catch (FirebaseMessagingException e) {
            LOG.errorf(e, "Erreur Firebase lors de l'envoi: %s", e.getErrorCode());
            
            // Gestion des erreurs spécifiques
            switch (e.getErrorCode()) {
                case "INVALID_ARGUMENT":
                    notification.setMessageErreur("Token FCM invalide");
                    break;
                case "UNREGISTERED":
                    notification.setMessageErreur("Token FCM non enregistré");
                    break;
                case "SENDER_ID_MISMATCH":
                    notification.setMessageErreur("Sender ID incorrect");
                    break;
                case "QUOTA_EXCEEDED":
                    notification.setMessageErreur("Quota Firebase dépassé");
                    break;
                default:
                    notification.setMessageErreur("Erreur Firebase: " + e.getErrorCode());
            }
            
            return false;
            
        } catch (Exception e) {
            LOG.errorf(e, "Erreur inattendue lors de l'envoi de notification");
            notification.setMessageErreur("Erreur technique: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * Envoie une notification push à plusieurs destinataires
     * 
     * @param notification La notification à envoyer
     * @param tokensFCM Liste des tokens FCM des destinataires
     * @return Résultat de l'envoi groupé
     */
    public BatchResponse envoyerNotificationGroupe(NotificationDTO notification, List<String> tokensFCM) {
        if (!initialized || !firebaseEnabled) {
            LOG.warn("Firebase non initialisé ou désactivé");
            return null;
        }
        
        try {
            // Filtrage des tokens valides
            List<String> tokensValides = tokensFCM.stream()
                .filter(token -> token != null && !token.isEmpty())
                .collect(Collectors.toList());
            
            if (tokensValides.isEmpty()) {
                LOG.warn("Aucun token FCM valide trouvé");
                return null;
            }
            
            // Construction du message multicast
            MulticastMessage message = construireMessageMulticast(notification, tokensValides);
            
            // Envoi par batch pour respecter les limites Firebase
            List<BatchResponse> responses = new ArrayList<>();
            
            for (int i = 0; i < tokensValides.size(); i += batchSize) {
                int fin = Math.min(i + batchSize, tokensValides.size());
                List<String> batch = tokensValides.subList(i, fin);
                
                MulticastMessage batchMessage = MulticastMessage.builder()
                    .setNotification(message.getNotification())
                    .setAndroidConfig(message.getAndroidConfig())
                    .setApnsConfig(message.getApnsConfig())
                    .setWebpushConfig(message.getWebpushConfig())
                    .putAllData(message.getData())
                    .addAllTokens(batch)
                    .build();
                
                BatchResponse response = firebaseMessaging.sendMulticast(batchMessage, dryRun);
                responses.add(response);
                
                LOG.infof("Batch envoyé: %d succès, %d échecs", 
                         response.getSuccessCount(), response.getFailureCount());
            }
            
            // Consolidation des résultats
            return consoliderResultatsBatch(responses);
            
        } catch (FirebaseMessagingException e) {
            LOG.errorf(e, "Erreur Firebase lors de l'envoi groupé: %s", e.getErrorCode());
            return null;
        } catch (Exception e) {
            LOG.errorf(e, "Erreur inattendue lors de l'envoi groupé");
            return null;
        }
    }
    
    /**
     * Envoie une notification à un topic Firebase
     * 
     * @param notification La notification à envoyer
     * @param topic Le topic Firebase
     * @return true si l'envoi a réussi
     */
    public boolean envoyerNotificationTopic(NotificationDTO notification, String topic) {
        if (!initialized || !firebaseEnabled) {
            LOG.warn("Firebase non initialisé ou désactivé");
            return false;
        }
        
        try {
            Message message = Message.builder()
                .setNotification(construireNotificationFirebase(notification))
                .setAndroidConfig(construireConfigAndroid(notification))
                .setApnsConfig(construireConfigApns(notification))
                .setWebpushConfig(construireConfigWebpush(notification))
                .putAllData(construireDonneesPersonnalisees(notification))
                .setTopic(topic)
                .build();
            
            String response = firebaseMessaging.send(message, dryRun);
            
            LOG.infof("Notification topic envoyée avec succès: %s", response);
            return true;
            
        } catch (FirebaseMessagingException e) {
            LOG.errorf(e, "Erreur Firebase lors de l'envoi au topic: %s", e.getErrorCode());
            return false;
        } catch (Exception e) {
            LOG.errorf(e, "Erreur inattendue lors de l'envoi au topic");
            return false;
        }
    }
    
    /**
     * Abonne un utilisateur à un topic
     * 
     * @param tokenFCM Token FCM de l'utilisateur
     * @param topic Topic à abonner
     * @return true si l'abonnement a réussi
     */
    public boolean abonnerAuTopic(String tokenFCM, String topic) {
        if (!initialized || !firebaseEnabled) {
            return false;
        }
        
        try {
            TopicManagementResponse response = firebaseMessaging
                .subscribeToTopic(List.of(tokenFCM), topic);
            
            LOG.infof("Abonnement au topic %s: %d succès, %d échecs", 
                     topic, response.getSuccessCount(), response.getFailureCount());
            
            return response.getSuccessCount() > 0;
            
        } catch (FirebaseMessagingException e) {
            LOG.errorf(e, "Erreur lors de l'abonnement au topic %s", topic);
            return false;
        }
    }
    
    /**
     * Désabonne un utilisateur d'un topic
     * 
     * @param tokenFCM Token FCM de l'utilisateur
     * @param topic Topic à désabonner
     * @return true si le désabonnement a réussi
     */
    public boolean desabonnerDuTopic(String tokenFCM, String topic) {
        if (!initialized || !firebaseEnabled) {
            return false;
        }
        
        try {
            TopicManagementResponse response = firebaseMessaging
                .unsubscribeFromTopic(List.of(tokenFCM), topic);
            
            LOG.infof("Désabonnement du topic %s: %d succès, %d échecs", 
                     topic, response.getSuccessCount(), response.getFailureCount());
            
            return response.getSuccessCount() > 0;
            
        } catch (FirebaseMessagingException e) {
            LOG.errorf(e, "Erreur lors du désabonnement du topic %s", topic);
            return false;
        }
    }
    
    // === MÉTHODES PRIVÉES ===
    
    /**
     * Construit un message Firebase pour un destinataire unique
     */
    private Message construireMessage(NotificationDTO notification, String tokenFCM) {
        return Message.builder()
            .setToken(tokenFCM)
            .setNotification(construireNotificationFirebase(notification))
            .setAndroidConfig(construireConfigAndroid(notification))
            .setApnsConfig(construireConfigApns(notification))
            .setWebpushConfig(construireConfigWebpush(notification))
            .putAllData(construireDonneesPersonnalisees(notification))
            .build();
    }
    
    /**
     * Construit un message multicast Firebase
     */
    private MulticastMessage construireMessageMulticast(NotificationDTO notification, List<String> tokens) {
        return MulticastMessage.builder()
            .addAllTokens(tokens)
            .setNotification(construireNotificationFirebase(notification))
            .setAndroidConfig(construireConfigAndroid(notification))
            .setApnsConfig(construireConfigApns(notification))
            .setWebpushConfig(construireConfigWebpush(notification))
            .putAllData(construireDonneesPersonnalisees(notification))
            .build();
    }
    
    /**
     * Construit la notification Firebase de base
     */
    private Notification construireNotificationFirebase(NotificationDTO notification) {
        return Notification.builder()
            .setTitle(notification.getTitre())
            .setBody(notification.getMessageCourt() != null ? 
                    notification.getMessageCourt() : notification.getMessage())
            .setImage(notification.getImageUrl())
            .build();
    }
    
    /**
     * Construit la configuration Android
     */
    private AndroidConfig construireConfigAndroid(NotificationDTO notification) {
        CanalNotification canal = notification.getCanal();
        
        AndroidNotification.Builder androidNotification = AndroidNotification.builder()
            .setTitle(notification.getTitre())
            .setBody(notification.getMessage())
            .setIcon(notification.getTypeNotification().getIcone())
            .setColor(notification.getTypeNotification().getCouleur())
            .setChannelId(canal.getId())
            .setPriority(AndroidNotification.Priority.valueOf(
                canal.isCritique() ? "HIGH" : "DEFAULT"))
            .setVisibility(AndroidNotification.Visibility.PUBLIC);
        
        // Configuration du son
        if (notification.getDoitEmettreSon()) {
            androidNotification.setSound(notification.getSonPersonnalise() != null ? 
                notification.getSonPersonnalise() : canal.getSonDefaut());
        }
        
        // Configuration des actions rapides
        if (notification.getActionsRapides() != null && !notification.getActionsRapides().isEmpty()) {
            // Les actions rapides Android nécessitent une configuration spéciale
            // Elles seront gérées côté client via les données personnalisées
        }
        
        return AndroidConfig.builder()
            .setNotification(androidNotification.build())
            .setPriority(canal.isCritique() ? AndroidConfig.Priority.HIGH : AndroidConfig.Priority.NORMAL)
            .setTtl(canal.getDureeVieMs())
            .build();
    }
    
    /**
     * Construit la configuration iOS (APNs)
     */
    private ApnsConfig construireConfigApns(NotificationDTO notification) {
        CanalNotification canal = notification.getCanal();
        
        Map<String, Object> apsData = new HashMap<>();
        apsData.put("alert", Map.of(
            "title", notification.getTitre(),
            "body", notification.getMessage()
        ));
        apsData.put("sound", notification.getDoitEmettreSon() ? "default" : null);
        apsData.put("badge", 1);
        
        if (notification.getDoitVibrer()) {
            apsData.put("vibrate", Arrays.toString(canal.getPatternVibration()));
        }
        
        return ApnsConfig.builder()
            .setAps(Aps.builder()
                .putAllCustomData(apsData)
                .build())
            .putHeader("apns-priority", canal.getPrioriteIOS())
            .putHeader("apns-expiration", String.valueOf(
                System.currentTimeMillis() + canal.getDureeVieMs()))
            .build();
    }
    
    /**
     * Construit la configuration Web Push
     */
    private WebpushConfig construireConfigWebpush(NotificationDTO notification) {
        Map<String, String> headers = new HashMap<>();
        headers.put("TTL", String.valueOf(notification.getCanal().getDureeVieMs() / 1000));
        
        Map<String, Object> notificationData = new HashMap<>();
        notificationData.put("title", notification.getTitre());
        notificationData.put("body", notification.getMessage());
        notificationData.put("icon", notification.getIconeUrl());
        notificationData.put("image", notification.getImageUrl());
        notificationData.put("badge", "/images/badge.png");
        notificationData.put("vibrate", notification.getCanal().getPatternVibration());
        
        // Actions rapides pour Web Push
        if (notification.getActionsRapides() != null) {
            List<Map<String, String>> actions = notification.getActionsRapides().stream()
                .map(action -> Map.of(
                    "action", action.getId(),
                    "title", action.getLibelle(),
                    "icon", action.getIconeParDefaut()
                ))
                .collect(Collectors.toList());
            notificationData.put("actions", actions);
        }
        
        return WebpushConfig.builder()
            .putAllHeaders(headers)
            .setNotification(notificationData)
            .build();
    }
    
    /**
     * Construit les données personnalisées
     */
    private Map<String, String> construireDonneesPersonnalisees(NotificationDTO notification) {
        Map<String, String> data = new HashMap<>();
        
        // Données de base
        data.put("notification_id", notification.getId());
        data.put("type", notification.getTypeNotification().name());
        data.put("canal", notification.getCanal().getId());
        data.put("action_clic", notification.getActionClic());
        
        // Paramètres d'action
        if (notification.getParametresAction() != null) {
            notification.getParametresAction().forEach(data::put);
        }
        
        // Données personnalisées
        if (notification.getDonneesPersonnalisees() != null) {
            notification.getDonneesPersonnalisees().forEach((key, value) -> 
                data.put(key, String.valueOf(value)));
        }
        
        // Actions rapides (sérialisées en JSON)
        if (notification.getActionsRapides() != null && !notification.getActionsRapides().isEmpty()) {
            // Sérialisation simplifiée des actions
            StringBuilder actionsJson = new StringBuilder("[");
            for (int i = 0; i < notification.getActionsRapides().size(); i++) {
                ActionNotificationDTO action = notification.getActionsRapides().get(i);
                if (i > 0) actionsJson.append(",");
                actionsJson.append(String.format(
                    "{\"id\":\"%s\",\"libelle\":\"%s\",\"type\":\"%s\"}", 
                    action.getId(), action.getLibelle(), action.getTypeAction()
                ));
            }
            actionsJson.append("]");
            data.put("actions_rapides", actionsJson.toString());
        }
        
        return data;
    }
    
    /**
     * Obtient le token FCM d'un utilisateur
     */
    private String obtenirTokenFCM(String utilisateurId) {
        // TODO: Implémenter la récupération du token FCM depuis la base de données
        // ou le service de préférences utilisateur
        return "token_fcm_exemple_" + utilisateurId;
    }
    
    /**
     * Consolide les résultats de plusieurs batch
     */
    private BatchResponse consoliderResultatsBatch(List<BatchResponse> responses) {
        // Implémentation simplifiée - dans un vrai projet, il faudrait
        // créer un BatchResponse personnalisé qui agrège tous les résultats
        return responses.isEmpty() ? null : responses.get(0);
    }
    
    /**
     * Vérifie si Firebase est initialisé
     */
    public boolean isInitialized() {
        return initialized && firebaseEnabled;
    }
}
