package dev.lions.unionflow.client.security;

import dev.lions.unionflow.client.dto.auth.LoginResponse;
import dev.lions.unionflow.client.service.AuthenticationService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

@ApplicationScoped
public class TokenRefreshService {
    
    private static final Logger LOGGER = Logger.getLogger(TokenRefreshService.class.getName());
    
    @Inject
    private AuthenticationService authService;
    
    // Stockage des tokens au niveau application pour éviter les problèmes de contexte session
    private final Map<String, TokenInfo> activeTokens = new ConcurrentHashMap<>();
    
    private static class TokenInfo {
        String accessToken;
        String refreshToken;
        long expirationTime;
        String sessionId;
        
        TokenInfo(String accessToken, String refreshToken, long expirationTime, String sessionId) {
            this.accessToken = accessToken;
            this.refreshToken = refreshToken;
            this.expirationTime = expirationTime;
            this.sessionId = sessionId;
        }
        
        boolean needsRefresh() {
            // Rafraîchir 5 minutes avant l'expiration
            long timeUntilExpiration = expirationTime - System.currentTimeMillis();
            return timeUntilExpiration < 300000; // 5 minutes en millisecondes
        }
        
        boolean isExpired() {
            return System.currentTimeMillis() > expirationTime;
        }
    }
    
    public void registerToken(String sessionId, String accessToken, String refreshToken, long expiresIn) {
        if (sessionId != null && accessToken != null) {
            long expirationTime = System.currentTimeMillis() + (expiresIn * 1000);
            activeTokens.put(sessionId, new TokenInfo(accessToken, refreshToken, expirationTime, sessionId));
            LOGGER.info("Token enregistré pour la session: " + sessionId);
        }
    }
    
    public void removeToken(String sessionId) {
        if (sessionId != null) {
            activeTokens.remove(sessionId);
            LOGGER.info("Token supprimé pour la session: " + sessionId);
        }
    }
    
    // Cette méthode n'est plus appelée par le scheduler pour éviter les problèmes de contexte
    // Elle peut être appelée manuellement depuis un contexte avec session active
    public void checkAndRefreshTokens(String sessionId) {
        try {
            TokenInfo tokenInfo = activeTokens.get(sessionId);
            
            if (tokenInfo != null && tokenInfo.needsRefresh() && tokenInfo.refreshToken != null) {
                LOGGER.info("Rafraîchissement du token JWT nécessaire pour session: " + sessionId);
                
                LoginResponse refreshedResponse = authService.refreshToken(tokenInfo.refreshToken);
                
                if (refreshedResponse != null) {
                    // Mettre à jour les tokens stockés
                    registerToken(sessionId, 
                                refreshedResponse.getAccessToken(),
                                refreshedResponse.getRefreshToken(),
                                refreshedResponse.getExpiresIn());
                    
                    LOGGER.info("Token JWT rafraîchi avec succès pour session: " + sessionId);
                } else {
                    LOGGER.warning("Échec du rafraîchissement du token JWT pour session: " + sessionId);
                    handleTokenRefreshFailure(sessionId);
                }
            }
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du rafraîchissement du token: " + e.getMessage());
            handleTokenRefreshFailure(sessionId);
        }
    }
    
    public boolean tryRefreshTokenNow(String sessionId) {
        try {
            TokenInfo tokenInfo = activeTokens.get(sessionId);
            
            if (tokenInfo != null && tokenInfo.refreshToken != null) {
                LoginResponse refreshedResponse = authService.refreshToken(tokenInfo.refreshToken);
                
                if (refreshedResponse != null) {
                    registerToken(sessionId,
                                refreshedResponse.getAccessToken(),
                                refreshedResponse.getRefreshToken(),
                                refreshedResponse.getExpiresIn());
                    
                    LOGGER.info("Token rafraîchi manuellement avec succès pour session: " + sessionId);
                    return true;
                }
            }
        } catch (Exception e) {
            LOGGER.warning("Échec du rafraîchissement manuel du token: " + e.getMessage());
        }
        
        return false;
    }
    
    private void handleTokenRefreshFailure(String sessionId) {
        // En cas d'échec du rafraîchissement, supprimer le token
        removeToken(sessionId);
        LOGGER.info("Session expirée - token supprimé pour: " + sessionId);
    }
    
    public boolean isTokenExpired(String sessionId) {
        TokenInfo tokenInfo = activeTokens.get(sessionId);
        return tokenInfo == null || tokenInfo.isExpired();
    }
    
    public long getTimeUntilExpiration(String sessionId) {
        TokenInfo tokenInfo = activeTokens.get(sessionId);
        if (tokenInfo != null) {
            long timeLeft = tokenInfo.expirationTime - System.currentTimeMillis();
            return Math.max(0, timeLeft / 1000); // Retourner en secondes
        }
        return 0;
    }
    
    public String getAccessToken(String sessionId) {
        TokenInfo tokenInfo = activeTokens.get(sessionId);
        return tokenInfo != null ? tokenInfo.accessToken : null;
    }
    
    // Méthode pour nettoyer les tokens expirés périodiquement
    public void cleanupExpiredTokens() {
        activeTokens.entrySet().removeIf(entry -> {
            boolean expired = entry.getValue().isExpired();
            if (expired) {
                LOGGER.info("Suppression du token expiré pour session: " + entry.getKey());
            }
            return expired;
        });
    }
}