package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.jboss.logging.Logger;

/**
 * Service de gestion des modules actifs par organisation.
 *
 * <p>Architecture Option C — les modules actifs sont déterminés par le TYPE
 * d'organisation, pas par le plan tarifaire. Le plan impacte uniquement la
 * profondeur fonctionnelle (reporting, API, fédération).
 *
 * <p>Les modules sont regroupés en deux catégories :
 * <ul>
 *   <li>MODULES_COMMUNS — accessibles à tous les types d'org</li>
 *   <li>Modules métier — spécifiques au type d'org (TONTINE, CREDIT, etc.)</li>
 * </ul>
 */
@ApplicationScoped
public class OrganisationModuleService {

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

    /** Modules présents sur toutes les organisations quelle que soit leur nature. */
    public static final Set<String> MODULES_COMMUNS = Set.of(
        "MEMBRES",
        "COTISATIONS",
        "EVENEMENTS",
        "COMMUNICATION",
        "DOCUMENTS",
        "NOTIFICATION",
        "AIDE"
    );

    @Inject
    OrganisationRepository organisationRepository;

    /**
     * Retourne l'ensemble des modules actifs pour une organisation donnée.
     * Combine les modules communs avec les modules métier du type d'org.
     */
    public Set<String> getModulesActifs(UUID organisationId) {
        Optional<Organisation> opt = organisationRepository.findByIdOptional(organisationId);
        if (opt.isEmpty()) {
            LOG.warnf("Organisation introuvable : %s", organisationId);
            return MODULES_COMMUNS;
        }
        return getModulesActifs(opt.get());
    }

    /**
     * Retourne l'ensemble des modules actifs pour une organisation.
     */
    public Set<String> getModulesActifs(Organisation organisation) {
        Set<String> modules = new LinkedHashSet<>(MODULES_COMMUNS);

        // 1. Modules issus du champ modulesActifs persisté (calculé depuis types_reference en V18)
        String modulesActifsCsv = organisation.getModulesActifs();
        if (modulesActifsCsv != null && !modulesActifsCsv.isBlank()) {
            Arrays.stream(modulesActifsCsv.split(","))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .forEach(modules::add);
            return modules;
        }

        // 2. Fallback : déduction depuis le typeOrganisation si modulesActifs non renseigné
        modules.addAll(getModulesParType(organisation.getTypeOrganisation()));
        return modules;
    }

    /**
     * Vérifie si un module spécifique est actif pour une organisation.
     */
    public boolean isModuleActif(UUID organisationId, String module) {
        return getModulesActifs(organisationId).contains(module.toUpperCase());
    }

    /**
     * Retourne les modules métier associés à un type d'organisation.
     * Utilisé en fallback si la colonne modules_actifs n'est pas peuplée.
     */
    public Set<String> getModulesParType(String typeOrganisation) {
        if (typeOrganisation == null) {
            return Collections.emptySet();
        }
        Set<String> modules = new LinkedHashSet<>();
        switch (typeOrganisation.toUpperCase()) {
            case "TONTINE" -> {
                modules.add("TONTINE");
                modules.add("FINANCE");
            }
            case "MUTUELLE", "MUTUELLE_EPARGNE", "MUTUELLE_CREDIT" -> {
                modules.add("EPARGNE");
                modules.add("CREDIT");
                modules.add("FINANCE");
                modules.add("LCB_FT");
            }
            case "COOPERATIVE" -> {
                modules.add("AGRICULTURE");
                modules.add("FINANCE");
            }
            case "ONG", "FONDATION" -> {
                modules.add("PROJETS_ONG");
                modules.add("COLLECTE_FONDS");
                modules.add("FINANCE");
            }
            case "EGLISE", "GROUPE_PRIERE" -> {
                modules.add("CULTE_DONS");
            }
            case "SYNDICAT", "ORDRE_PROFESSIONNEL", "FEDERATION" -> {
                modules.add("VOTES");
                modules.add("REGISTRE_AGREMENT");
            }
            case "GIE" -> {
                modules.add("FINANCE");
            }
            case "ASSOCIATION", "CLUB_SERVICE" -> {
                modules.add("TONTINE");
                modules.add("VOTES");
            }
            case "CLUB_SPORTIF", "CLUB_CULTUREL" -> {
                modules.add("VOTES");
            }
            default -> LOG.debugf("Type d''organisation non reconnu pour module mapping : %s", typeOrganisation);
        }
        return modules;
    }

    /**
     * Retourne la liste des modules actifs sous forme de tableau JSON-friendly.
     * Utilisé par l'endpoint /api/organisations/{id}/modules-actifs
     */
    public ModulesActifsResponse getModulesActifsResponse(UUID organisationId) {
        Optional<Organisation> opt = organisationRepository.findByIdOptional(organisationId);
        if (opt.isEmpty()) {
            return new ModulesActifsResponse(organisationId, Collections.emptySet(), "UNKNOWN");
        }
        Organisation org = opt.get();
        Set<String> modules = getModulesActifs(org);
        return new ModulesActifsResponse(organisationId, modules, org.getTypeOrganisation());
    }

    /** DTO de réponse pour l'endpoint modules-actifs. */
    public record ModulesActifsResponse(UUID organisationId, Set<String> modules, String typeOrganisation) {}
}
