package dev.lions.unionflow.client.service;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import org.jboss.logging.Logger;

/**
 * Service centralisé de gestion des erreurs et messages utilisateur.
 * 
 * <p>Ce service fournit une interface unifiée pour :
 * <ul>
 *   <li>Gérer les erreurs backend de manière cohérente</li>
 *   <li>Afficher des messages utilisateur appropriés</li>
 *   <li>Logger les erreurs de manière structurée</li>
 *   <li>Gérer les cas d'erreur spécifiques (connexion, autorisation, validation, etc.)</li>
 * </ul>
 * 
 * <p><strong>Production-ready:</strong> Gestion complète des erreurs avec logging structuré,
 * messages utilisateur appropriés, et gestion des cas limites.
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-12-24
 */
@ApplicationScoped
public class ErrorHandlerService {

    private static final Logger LOG = Logger.getLogger(ErrorHandlerService.class);
    
    @Inject
    FacesContext facesContext;
    
    /**
     * Gère une exception backend et affiche un message approprié à l'utilisateur.
     * 
     * @param exception L'exception à gérer
     * @param contextMessage Message contextuel pour le logging (ex: "lors de la création d'une organisation")
     * @param userFriendlyMessage Message affiché à l'utilisateur (peut être null pour utiliser le message par défaut)
     */
    public void handleException(Exception exception, String contextMessage, String userFriendlyMessage) {
        if (exception == null) {
            LOG.warn("handleException appelé avec exception null pour: " + contextMessage);
            return;
        }
        
        // Logging structuré de l'erreur
        logError(exception, contextMessage);
        
        // Déterminer le type d'erreur et le message approprié
        FacesMessage.Severity severity = FacesMessage.SEVERITY_ERROR;
        String message = userFriendlyMessage != null ? userFriendlyMessage : getDefaultErrorMessage(exception);
        String detail = getErrorDetail(exception);
        
        // Gestion spécifique selon le type d'exception
        if (exception instanceof RestClientExceptionMapper.UnauthorizedException) {
            severity = FacesMessage.SEVERITY_ERROR;
            message = "Erreur d'autorisation";
            detail = "Vous n'êtes pas autorisé à effectuer cette action. Veuillez vérifier vos permissions.";
        } else if (exception instanceof RestClientExceptionMapper.ForbiddenException) {
            severity = FacesMessage.SEVERITY_ERROR;
            message = "Accès interdit";
            detail = "Vous n'avez pas les permissions nécessaires pour accéder à cette ressource.";
        } else if (exception instanceof RestClientExceptionMapper.BadRequestException) {
            severity = FacesMessage.SEVERITY_ERROR;
            message = "Données invalides";
            detail = extractValidationErrors(exception.getMessage());
        } else if (exception instanceof RestClientExceptionMapper.ConflictException) {
            severity = FacesMessage.SEVERITY_WARN;
            message = "Conflit";
            detail = "Cette ressource existe déjà ou est en conflit avec une autre.";
        } else if (exception instanceof RestClientExceptionMapper.NotFoundException) {
            severity = FacesMessage.SEVERITY_WARN;
            message = "Ressource introuvable";
            detail = "La ressource demandée n'existe pas ou a été supprimée.";
        } else if (exception instanceof RestClientExceptionMapper.UnprocessableEntityException) {
            severity = FacesMessage.SEVERITY_ERROR;
            message = "Données non valides";
            detail = extractValidationErrors(exception.getMessage());
        } else if (exception instanceof jakarta.ws.rs.ProcessingException) {
            severity = FacesMessage.SEVERITY_ERROR;
            message = "Erreur de connexion";
            detail = "Impossible de se connecter au serveur. Vérifiez votre connexion réseau et que le serveur est démarré.";
        } else if (exception instanceof java.net.ConnectException || 
                   exception.getCause() instanceof java.net.ConnectException) {
            severity = FacesMessage.SEVERITY_ERROR;
            message = "Serveur inaccessible";
            detail = "Le serveur backend n'est pas accessible. Vérifiez qu'il est démarré et accessible.";
        } else if (exception instanceof java.util.concurrent.TimeoutException ||
                   exception.getCause() instanceof java.util.concurrent.TimeoutException) {
            severity = FacesMessage.SEVERITY_WARN;
            message = "Délai d'attente dépassé";
            detail = "La requête a pris trop de temps. Veuillez réessayer.";
        }
        
        // Afficher le message à l'utilisateur
        addFacesMessage(severity, message, detail);
    }
    
    /**
     * Gère une erreur de validation et affiche les messages appropriés.
     * 
     * @param validationErrors Liste des erreurs de validation
     * @param contextMessage Message contextuel pour le logging
     */
    public void handleValidationErrors(java.util.List<String> validationErrors, String contextMessage) {
        if (validationErrors == null || validationErrors.isEmpty()) {
            return;
        }
        
        LOG.warnf("Erreurs de validation %s: %d erreur(s)", contextMessage, validationErrors.size());
        
        // Afficher chaque erreur de validation
        for (String error : validationErrors) {
            addFacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur de validation", error);
        }
    }
    
    /**
     * Affiche un message de succès à l'utilisateur.
     * 
     * @param summary Résumé du message
     * @param detail Détail du message
     */
    public void showSuccess(String summary, String detail) {
        addFacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
    }
    
    /**
     * Affiche un message d'information à l'utilisateur.
     * 
     * @param summary Résumé du message
     * @param detail Détail du message
     */
    public void showInfo(String summary, String detail) {
        addFacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
    }
    
    /**
     * Affiche un message d'avertissement à l'utilisateur.
     * 
     * @param summary Résumé du message
     * @param detail Détail du message
     */
    public void showWarning(String summary, String detail) {
        addFacesMessage(FacesMessage.SEVERITY_WARN, summary, detail);
    }
    
    /**
     * Log l'erreur de manière structurée.
     */
    private void logError(Exception exception, String contextMessage) {
        String errorId = java.util.UUID.randomUUID().toString().substring(0, 8);
        
        LOG.errorf("=== ERREUR [%s] ===", errorId);
        LOG.errorf("Contexte: %s", contextMessage);
        LOG.errorf("Type: %s", exception.getClass().getName());
        LOG.errorf("Message: %s", exception.getMessage());
        
        if (exception.getCause() != null) {
            LOG.errorf("Cause: %s - %s", 
                exception.getCause().getClass().getName(), 
                exception.getCause().getMessage());
        }
        
        // Stack trace complet en mode DEBUG seulement
        if (LOG.isDebugEnabled()) {
            LOG.errorf("Stack trace:", exception);
        } else {
            // En production, logger seulement les 5 premières lignes du stack trace
            StackTraceElement[] stackTrace = exception.getStackTrace();
            int maxLines = Math.min(5, stackTrace.length);
            for (int i = 0; i < maxLines; i++) {
                LOG.errorf("  at %s", stackTrace[i]);
            }
        }
        
        LOG.errorf("=== FIN ERREUR [%s] ===", errorId);
    }
    
    /**
     * Obtient le message d'erreur par défaut selon le type d'exception.
     */
    private String getDefaultErrorMessage(Exception exception) {
        if (exception.getMessage() != null && !exception.getMessage().trim().isEmpty()) {
            return exception.getMessage();
        }
        return "Une erreur inattendue s'est produite. Veuillez réessayer.";
    }
    
    /**
     * Extrait les détails d'erreur de validation depuis le message d'exception.
     */
    private String extractValidationErrors(String errorMessage) {
        if (errorMessage == null || errorMessage.trim().isEmpty()) {
            return "Les données fournies ne sont pas valides.";
        }
        
        // Si le message contient du JSON (erreur de validation Bean Validation)
        if (errorMessage.contains("\"objectName\"") || errorMessage.contains("\"attributeName\"")) {
            try {
                // Essayer d'extraire les informations de validation
                if (errorMessage.contains("\"attributeName\"")) {
                    // Extraire le nom de l'attribut
                    int attrIndex = errorMessage.indexOf("\"attributeName\"");
                    if (attrIndex > 0) {
                        int start = errorMessage.indexOf("\"", attrIndex + 16) + 1;
                        int end = errorMessage.indexOf("\"", start);
                        if (start > 0 && end > start) {
                            String attributeName = errorMessage.substring(start, end);
                            return "Le champ '" + attributeName + "' contient une valeur invalide.";
                        }
                    }
                }
            } catch (Exception e) {
                LOG.debugf("Impossible d'extraire les détails de validation: %s", e.getMessage());
            }
        }
        
        return errorMessage;
    }
    
    /**
     * Obtient le détail de l'erreur pour l'affichage à l'utilisateur.
     */
    private String getErrorDetail(Exception exception) {
        String message = exception.getMessage();
        
        // Pour les erreurs de validation, extraire les détails
        if (exception instanceof RestClientExceptionMapper.BadRequestException ||
            exception instanceof RestClientExceptionMapper.UnprocessableEntityException) {
            return extractValidationErrors(message);
        }
        
        // Pour les autres erreurs, utiliser le message de l'exception
        if (message != null && !message.trim().isEmpty()) {
            // Limiter la longueur du message pour l'utilisateur
            if (message.length() > 200) {
                return message.substring(0, 197) + "...";
            }
            return message;
        }
        
        return "Une erreur technique s'est produite. Veuillez contacter le support si le problème persiste.";
    }
    
    /**
     * Ajoute un message Faces avec gestion du Flash Scope pour les redirections.
     */
    private void addFacesMessage(FacesMessage.Severity severity, String summary, String detail) {
        if (facesContext == null) {
            facesContext = FacesContext.getCurrentInstance();
        }
        
        if (facesContext != null) {
            FacesMessage message = new FacesMessage(severity, summary, detail);
            facesContext.addMessage(null, message);
            
            // Activer le Flash Scope pour que le message survive à une redirection
            facesContext.getExternalContext().getFlash().setKeepMessages(true);
        }
    }
    
    /**
     * Vérifie si une exception est une erreur de connexion au backend.
     */
    public boolean isConnectionError(Exception exception) {
        return exception instanceof jakarta.ws.rs.ProcessingException ||
               exception instanceof java.net.ConnectException ||
               (exception.getCause() != null && exception.getCause() instanceof java.net.ConnectException) ||
               exception instanceof java.util.concurrent.TimeoutException ||
               (exception.getCause() != null && exception.getCause() instanceof java.util.concurrent.TimeoutException);
    }
    
    /**
     * Vérifie si une exception est une erreur d'autorisation.
     */
    public boolean isAuthorizationError(Exception exception) {
        return exception instanceof RestClientExceptionMapper.UnauthorizedException ||
               exception instanceof RestClientExceptionMapper.ForbiddenException;
    }
    
    /**
     * Vérifie si une exception est une erreur de validation.
     */
    public boolean isValidationError(Exception exception) {
        return exception instanceof RestClientExceptionMapper.BadRequestException ||
               exception instanceof RestClientExceptionMapper.UnprocessableEntityException;
    }
}

