package dev.lions.unionflow.server.security;

import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.MembreOrganisation;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.MembreOrganisationRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.service.MembreRoleSyncService;
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.ext.Provider;
import java.io.IOException;
import java.util.Optional;
import java.util.UUID;
import org.jboss.logging.Logger;

/**
 * Filtre JAX-RS qui résout l'organisation active pour chaque requête.
 *
 * <p>Lit le header {@code X-Active-Organisation-Id}, valide que l'utilisateur
 * connecté est bien membre actif de cette organisation, puis peuple
 * {@link OrganisationContextHolder} pour les services aval.
 *
 * <p>Si le header est absent, le contexte reste non résolu (les endpoints
 * qui ne nécessitent pas de contexte org continuent de fonctionner normalement).
 * Si le header est présent mais invalide ou si l'utilisateur n'est pas membre,
 * la requête est rejetée avec HTTP 403.
 *
 * <p>Priorité : AUTHORIZATION + 5 (après auth, avant @RequiresModule à +10).
 */
@Provider
@Priority(Priorities.AUTHORIZATION + 5)
public class OrganisationContextFilter implements ContainerRequestFilter {

    private static final Logger LOG = Logger.getLogger(OrganisationContextFilter.class);
    public static final String HEADER_ACTIVE_ORG = ModuleAccessFilter.HEADER_ACTIVE_ORG;

    @Inject
    OrganisationContextHolder contextHolder;

    @Inject
    SecurityIdentity securityIdentity;

    @Inject
    MembreRepository membreRepository;

    @Inject
    MembreOrganisationRepository membreOrganisationRepository;

    @Inject
    OrganisationRepository organisationRepository;

    @Inject
    MembreRoleSyncService membreRoleSyncService;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        String orgIdHeader = requestContext.getHeaderString(HEADER_ACTIVE_ORG);

        if (orgIdHeader == null || orgIdHeader.isBlank()) {
            // Pas de contexte org — les endpoints sans contexte fonctionnent normalement
            return;
        }

        // Valider le format UUID
        UUID organisationId;
        try {
            organisationId = UUID.fromString(orgIdHeader.trim());
        } catch (IllegalArgumentException e) {
            LOG.warnf("Header %s invalide : %s", HEADER_ACTIVE_ORG, orgIdHeader);
            requestContext.abortWith(jakarta.ws.rs.core.Response
                .status(jakarta.ws.rs.core.Response.Status.BAD_REQUEST)
                .entity(java.util.Map.of("error", "INVALID_ORGANISATION_ID",
                    "message", "Le header X-Active-Organisation-Id n'est pas un UUID valide."))
                .build());
            return;
        }

        // Résoudre l'organisation
        Optional<Organisation> orgOpt = organisationRepository.findByIdOptional(organisationId);
        if (orgOpt.isEmpty()) {
            LOG.warnf("Organisation introuvable : %s", organisationId);
            requestContext.abortWith(jakarta.ws.rs.core.Response
                .status(jakarta.ws.rs.core.Response.Status.NOT_FOUND)
                .entity(java.util.Map.of("error", "ORGANISATION_NOT_FOUND",
                    "message", "Organisation introuvable."))
                .build());
            return;
        }

        Organisation organisation = orgOpt.get();

        // Vérifier l'appartenance (skip pour SUPERADMIN qui a accès à tout)
        if (!securityIdentity.isAnonymous()) {
            boolean isSuperAdmin = securityIdentity.getRoles().contains("SUPERADMIN")
                || securityIdentity.getRoles().contains("SUPER_ADMIN")
                || securityIdentity.getRoles().contains("ADMIN");

            if (!isSuperAdmin) {
                String email = securityIdentity.getPrincipal().getName();
                Optional<Membre> membreOpt = membreRepository.findByEmail(email);

                if (membreOpt.isPresent()) {
                    Optional<MembreOrganisation> membreOrgOpt = membreOrganisationRepository
                        .findByMembreIdAndOrganisationId(membreOpt.get().getId(), organisationId);

                    if (membreOrgOpt.isEmpty()) {
                        LOG.warnf("Utilisateur %s n'est pas membre de l'organisation %s", email, organisationId);
                        requestContext.abortWith(jakarta.ws.rs.core.Response
                            .status(jakarta.ws.rs.core.Response.Status.FORBIDDEN)
                            .entity(java.util.Map.of("error", "NOT_MEMBER_OF_ORGANISATION",
                                "message", "Vous n'êtes pas membre de cette organisation."))
                            .build());
                        return;
                    }

                    // Vérifier que le membre est actif
                    MembreOrganisation membreOrg = membreOrgOpt.get();
                    String statut = membreOrg.getStatutMembre() != null
                        ? membreOrg.getStatutMembre().name() : "";
                    if (!"ACTIF".equals(statut)) {
                        LOG.warnf("Membre %s statut non-actif (%s) dans l'organisation %s", email, statut, organisationId);
                        requestContext.abortWith(jakarta.ws.rs.core.Response
                            .status(jakarta.ws.rs.core.Response.Status.FORBIDDEN)
                            .entity(java.util.Map.of("error", "MEMBER_NOT_ACTIVE",
                                "message", "Votre adhésion à cette organisation n'est pas active."))
                            .build());
                        return;
                    }

                    // Sync rôle DB ↔ Keycloak : si l'utilisateur est admin dans Keycloak
                    // mais n'a pas encore de MembreRole ORGADMIN en base, le créer.
                    if (securityIdentity.getRoles().contains("ADMIN_ORGANISATION")) {
                        try {
                            membreRoleSyncService.ensureOrgAdminRole(membreOrg);
                        } catch (Exception e) {
                            // Non bloquant — on log et on continue
                            LOG.warnf("ensureOrgAdminRole: erreur non bloquante pour %s : %s",
                                email, e.getMessage());
                        }
                    }
                }
            }
        }

        // Peupler le contexte
        contextHolder.setOrganisationId(organisationId);
        contextHolder.setOrganisation(organisation);
        contextHolder.setResolved(true);
        LOG.debugf("Contexte org résolu : %s (%s)", organisation.getNom(), organisationId);
    }
}
