package dev.lions.unionflow.client.interceptor;

import dev.lions.unionflow.client.service.MetricsService;
import jakarta.annotation.Priority;
import jakarta.inject.Inject;
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;
import org.jboss.logging.Logger;

/**
 * Intercepteur CDI pour logger automatiquement les appels aux services backend.
 * 
 * <p>Cet intercepteur permet de tracer automatiquement tous les appels REST Client
 * vers le backend, avec logging structuré incluant:
 * - Méthode appelée
 * - Paramètres (masqués si sensibles)
 * - Temps d'exécution
 * - Résultat ou erreur
 * 
 * <p><strong>Production-ready:</strong> Logging structuré, gestion des erreurs,
 * masquage des données sensibles, et métriques de performance.
 * 
 * <p><strong>Usage:</strong>
 * <pre>{@code
 * @LogBackendCall
 * public OrganisationResponse creer(OrganisationResponse association) {
 *     // L'appel sera automatiquement loggé
 * }
 * }</pre>
 * 
 * @author UnionFlow Team
 * @version 3.0
 * @since 2025-12-24
 */
@Interceptor
@LogBackendCall
@Priority(1000)
public class BackendCallInterceptor {

    private static final Logger LOG = Logger.getLogger(BackendCallInterceptor.class);
    
    @Inject
    MetricsService metricsService;

    @AroundInvoke
    public Object intercept(InvocationContext context) throws Exception {
        String methodName = context.getMethod().getName();
        String className = context.getTarget().getClass().getSimpleName();
        String serviceName = className + "." + methodName;
        long startTime = System.currentTimeMillis();
        
        // Logger l'appel
        LOG.debugf("=== APPEL BACKEND [%s.%s] ===", className, methodName);
        
        // Logger les paramètres (masqués si sensibles)
        Object[] parameters = context.getParameters();
        if (parameters != null && parameters.length > 0) {
            for (int i = 0; i < parameters.length; i++) {
                Object param = parameters[i];
                String paramValue = maskSensitiveData(param);
                LOG.debugf("  Param[%d]: %s", i, paramValue);
            }
        }
        
        try {
            // Exécuter la méthode
            Object result = context.proceed();
            
            // Calculer le temps d'exécution
            long duration = System.currentTimeMillis() - startTime;
            
            // Logger le résultat
            String resultSummary = summarizeResult(result);
            LOG.infof("=== SUCCÈS [%s.%s] - Durée: %d ms - Résultat: %s ===", 
                className, methodName, duration, resultSummary);
            
            // Logger un avertissement si la durée est excessive
            if (duration > 5000) {
                LOG.warnf("Appel backend lent détecté: %s.%s a pris %d ms", className, methodName, duration);
            }
            
            // Enregistrer les métriques
            if (metricsService != null) {
                metricsService.recordBackendCall(serviceName, true, duration);
            }
            
            return result;
            
        } catch (Exception e) {
            // Calculer le temps d'exécution même en cas d'erreur
            long duration = System.currentTimeMillis() - startTime;
            
            // Logger l'erreur
            LOG.errorf(e, "=== ERREUR [%s.%s] - Durée: %d ms - Exception: %s ===", 
                className, methodName, duration, e.getClass().getSimpleName());
            
            // Enregistrer les métriques d'erreur
            if (metricsService != null) {
                metricsService.recordBackendCall(serviceName, false, duration);
            }
            
            // Re-lancer l'exception
            throw e;
        }
    }
    
    /**
     * Masque les données sensibles dans les logs.
     */
    private String maskSensitiveData(Object param) {
        if (param == null) {
            return "null";
        }
        
        String paramStr = param.toString();
        
        // Masquer les tokens JWT
        if (paramStr.contains("Bearer ") || paramStr.length() > 100 && paramStr.matches("^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$")) {
            return "***TOKEN_MASQUÉ***";
        }
        
        // Masquer les mots de passe
        if (paramStr.toLowerCase().contains("password") || paramStr.toLowerCase().contains("motdepasse")) {
            return "***PASSWORD_MASQUÉ***";
        }
        
        // Limiter la longueur pour éviter les logs trop longs
        if (paramStr.length() > 500) {
            return paramStr.substring(0, 497) + "...";
        }
        
        return paramStr;
    }
    
    /**
     * Résume le résultat pour le logging.
     */
    private String summarizeResult(Object result) {
        if (result == null) {
            return "null";
        }
        
        if (result instanceof java.util.List) {
            java.util.List<?> list = (java.util.List<?>) result;
            return String.format("Liste de %d élément(s)", list.size());
        }
        
        if (result instanceof java.util.Collection) {
            java.util.Collection<?> collection = (java.util.Collection<?>) result;
            return String.format("Collection de %d élément(s)", collection.size());
        }
        
        // Pour les DTOs, afficher un résumé
        String className = result.getClass().getSimpleName();
        if (className.endsWith("DTO")) {
            try {
                // Essayer d'obtenir un ID ou un nom pour le résumé
                java.lang.reflect.Method getIdMethod = result.getClass().getMethod("getId");
                Object id = getIdMethod.invoke(result);
                if (id != null) {
                    return String.format("%s(id=%s)", className, id);
                }
            } catch (Exception e) {
                // Ignorer si la méthode n'existe pas
            }
            
            try {
                java.lang.reflect.Method getNomMethod = result.getClass().getMethod("getNom");
                Object nom = getNomMethod.invoke(result);
                if (nom != null) {
                    return String.format("%s(nom=%s)", className, nom);
                }
            } catch (Exception e) {
                // Ignorer si la méthode n'existe pas
            }
        }
        
        return className;
    }
    
}

