package dev.lions.unionflow.server.filter;

import dev.lions.unionflow.server.service.SystemLoggingService;
import jakarta.annotation.Priority;
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.security.Principal;

/**
 * Filtre JAX-RS pour capturer toutes les requêtes HTTP et les persister dans system_logs.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-15
 */
@Slf4j
@Provider
@Priority(1000)
public class HttpLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {

    private static final String REQUEST_START_TIME = "REQUEST_START_TIME";
    private static final String REQUEST_METHOD = "REQUEST_METHOD";
    private static final String REQUEST_PATH = "REQUEST_PATH";

    @Inject
    SystemLoggingService systemLoggingService;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // Enregistrer le timestamp de début de requête
        requestContext.setProperty(REQUEST_START_TIME, System.currentTimeMillis());
        requestContext.setProperty(REQUEST_METHOD, requestContext.getMethod());
        requestContext.setProperty(REQUEST_PATH, requestContext.getUriInfo().getPath());
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        try {
            // Calculer la durée de la requête
            Long startTime = (Long) requestContext.getProperty(REQUEST_START_TIME);
            long durationMs = startTime != null ? System.currentTimeMillis() - startTime : 0;

            // Récupérer les informations de la requête
            String method = (String) requestContext.getProperty(REQUEST_METHOD);
            String path = (String) requestContext.getProperty(REQUEST_PATH);
            int statusCode = responseContext.getStatus();

            // Récupérer l'utilisateur connecté
            String userId = extractUserId(requestContext);

            // Récupérer l'IP
            String ipAddress = extractIpAddress(requestContext);

            // Récupérer le sessionId (optionnel)
            String sessionId = extractSessionId(requestContext);

            // Ne logger que les endpoints API (ignorer /q/*, /static/*, etc.)
            if (shouldLog(path)) {
                systemLoggingService.logRequest(
                        method,
                        "/" + path,
                        statusCode,
                        userId,
                        ipAddress,
                        sessionId,
                        durationMs
                );
            }

        } catch (Exception e) {
            // Ne jamais laisser le logging casser l'application
            log.error("Error in HttpLoggingFilter", e);
        }
    }

    /**
     * Extraire l'ID utilisateur depuis le contexte de sécurité
     */
    private String extractUserId(ContainerRequestContext requestContext) {
        SecurityContext securityContext = requestContext.getSecurityContext();
        if (securityContext != null) {
            Principal principal = securityContext.getUserPrincipal();
            if (principal != null) {
                return principal.getName();
            }
        }
        return "anonymous";
    }

    /**
     * Extraire l'adresse IP du client
     */
    private String extractIpAddress(ContainerRequestContext requestContext) {
        // Essayer d'abord les headers de proxy
        String xForwardedFor = requestContext.getHeaderString("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            // Prendre la première IP de la liste
            return xForwardedFor.split(",")[0].trim();
        }

        String xRealIp = requestContext.getHeaderString("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }

        // Sinon retourner "unknown"
        return "unknown";
    }

    /**
     * Extraire le session ID (si disponible)
     */
    private String extractSessionId(ContainerRequestContext requestContext) {
        // Essayer de récupérer depuis les cookies ou headers
        String sessionId = requestContext.getHeaderString("X-Session-ID");
        if (sessionId != null && !sessionId.isEmpty()) {
            return sessionId;
        }

        // Par défaut, retourner null
        return null;
    }

    /**
     * Déterminer si on doit logger cette requête
     * Ignorer les endpoints techniques (health, metrics, swagger, etc.)
     */
    private boolean shouldLog(String path) {
        if (path == null) {
            return false;
        }

        // Ignorer les endpoints techniques Quarkus
        if (path.startsWith("q/")) {
            return false;
        }

        // Ignorer les ressources statiques
        if (path.startsWith("static/") || path.startsWith("webjars/")) {
            return false;
        }

        // Logger uniquement les endpoints API
        return path.startsWith("api/");
    }
}
