package dev.lions.unionflow.server.security;

import dev.lions.unionflow.server.service.OrganisationModuleService;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.annotation.Priority;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ResourceInfo;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import org.jboss.logging.Logger;

/**
 * Filtre JAX-RS qui applique le contrôle d'accès par module métier.
 *
 * <p>S'exécute après l'authentification (AUTHORIZATION + 10) pour laisser
 * Quarkus OIDC et {@code @RolesAllowed} s'exécuter en premier.
 *
 * <p>Le filtre :
 * <ol>
 *   <li>Détecte l'annotation {@link RequiresModule} sur la méthode ou la classe.</li>
 *   <li>Extrait l'organisation active depuis le header {@code X-Active-Organisation-Id}.</li>
 *   <li>Vérifie via {@link OrganisationModuleService} que le module est activé.</li>
 *   <li>Retourne HTTP 403 avec un message explicite si le module est absent.</li>
 * </ol>
 *
 * <p>Header attendu : {@code X-Active-Organisation-Id: <UUID>}
 */
@Provider
@Priority(Priorities.AUTHORIZATION + 10)
public class ModuleAccessFilter implements ContainerRequestFilter {

    private static final Logger LOG = Logger.getLogger(ModuleAccessFilter.class);

    /** Nom du header HTTP transportant l'UUID de l'organisation active. */
    public static final String HEADER_ACTIVE_ORG = "X-Active-Organisation-Id";

    @Inject
    OrganisationModuleService organisationModuleService;

    @Inject
    SecurityIdentity identity;

    @Context
    ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // 1. Détecter @RequiresModule sur la méthode, puis sur la classe
        Method method = resourceInfo.getResourceMethod();
        RequiresModule annotation = method != null ? method.getAnnotation(RequiresModule.class) : null;
        if (annotation == null && resourceInfo.getResourceClass() != null) {
            annotation = resourceInfo.getResourceClass().getAnnotation(RequiresModule.class);
        }

        if (annotation == null) {
            // Aucune annotation — laisser passer
            return;
        }

        // SUPER_ADMIN a accès global à tous les modules sans contexte d'organisation
        if (identity.hasRole("SUPER_ADMIN")) {
            return;
        }

        String moduleRequis = annotation.value().toUpperCase();

        // 2. Extraire l'organisation active depuis le header
        String orgIdHeader = requestContext.getHeaderString(HEADER_ACTIVE_ORG);
        if (orgIdHeader == null || orgIdHeader.isBlank()) {
            LOG.warnf("@RequiresModule(%s) — header %s absent, accès refusé", moduleRequis, HEADER_ACTIVE_ORG);
            requestContext.abortWith(buildForbiddenResponse(
                "Le header " + HEADER_ACTIVE_ORG + " est requis pour accéder à ce module.",
                moduleRequis));
            return;
        }

        UUID organisationId;
        try {
            organisationId = UUID.fromString(orgIdHeader.trim());
        } catch (IllegalArgumentException e) {
            LOG.warnf("@RequiresModule(%s) — header %s invalide : %s", moduleRequis, HEADER_ACTIVE_ORG, orgIdHeader);
            requestContext.abortWith(buildForbiddenResponse(
                "La valeur du header " + HEADER_ACTIVE_ORG + " n'est pas un UUID valide.",
                moduleRequis));
            return;
        }

        // 3. Vérifier l'activation du module
        if (!organisationModuleService.isModuleActif(organisationId, moduleRequis)) {
            String message = annotation.message().isBlank()
                ? "Le module '" + moduleRequis + "' n'est pas activé pour cette organisation."
                : annotation.message();
            LOG.infof("@RequiresModule(%s) — module inactif pour l'organisation %s", moduleRequis, organisationId);
            requestContext.abortWith(buildForbiddenResponse(message, moduleRequis));
        }
    }

    private Response buildForbiddenResponse(String message, String module) {
        return Response.status(Response.Status.FORBIDDEN)
            .entity(Map.of(
                "error", "MODULE_NOT_ACTIVE",
                "message", message,
                "module", module
            ))
            .build();
    }
}
