package dev.lions.unionflow.client.service;

import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationResponse;
import dev.lions.unionflow.server.api.dto.reference.response.TypeReferenceResponse;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.model.SelectItem;
import jakarta.inject.Inject;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Service applicatif (singleton) résolvant les libellés des types d'organisation
 * à partir du catalogue dynamique stocké en base.
 *
 * <p>Remplace tous les {@code switch} hardcodés répandus dans les beans JSF.
 * Le cache est invalidé explicitement après chaque mutation du catalogue
 * ({@link #recharger()}).
 */
@ApplicationScoped
public class TypeCatalogueService {

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

    @Inject
    @RestClient
    TypeOrganisationClientService typeOrganisationClientService;

    /** code → libelle, chargé paresseusement. */
    private volatile Map<String, String> libelleCache;
    /** liste complète (actifs + inactifs) pour les formulaires admin. */
    private volatile List<TypeReferenceResponse> catalogueCache;

    // ── Chargement ──────────────────────────────────────────────────────────

    private synchronized void charger() {
        if (libelleCache != null) return; // double-checked locking
        try {
            List<TypeReferenceResponse> types = typeOrganisationClientService.list(false);
            Map<String, String> map = new LinkedHashMap<>();
            for (TypeReferenceResponse t : types) {
                if (t.getCode() != null && t.getLibelle() != null) {
                    map.put(t.getCode(), t.getLibelle());
                }
            }
            catalogueCache = List.copyOf(types);
            libelleCache   = Collections.unmodifiableMap(map);
            LOG.infof("Catalogue types chargé : %d entrées", map.size());
        } catch (Exception e) {
            LOG.errorf(e, "Impossible de charger le catalogue des types d'organisation");
            catalogueCache = List.of();
            libelleCache   = Map.of();
        }
    }

    /** Invalide le cache — à appeler après toute mutation du catalogue. */
    public void recharger() {
        libelleCache   = null;
        catalogueCache = null;
        LOG.debug("Cache catalogue types invalidé");
    }

    // ── Résolution ──────────────────────────────────────────────────────────

    /**
     * Retourne le libellé correspondant au code, ou le code brut si inconnu.
     * Ne retourne jamais {@code null}.
     */
    public String resolveLibelle(String code) {
        if (code == null || code.isBlank()) return "";
        if (libelleCache == null) charger();
        return libelleCache.getOrDefault(code, code);
    }

    /**
     * Renseigne {@code typeOrganisationLibelle} sur chaque DTO de la liste d'après le catalogue.
     * Opération in-place, O(n).
     */
    public void enrichir(List<OrganisationResponse> dtos) {
        if (dtos == null || dtos.isEmpty()) return;
        if (libelleCache == null) charger();
        for (OrganisationResponse dto : dtos) {
            dto.setTypeOrganisationLibelle(resolveLibelle(dto.getTypeOrganisation()));
        }
    }

    // ── Listes pour les composants UI ────────────────────────────────────────

    /**
     * Retourne les types actifs sous forme de {@link SelectItem} avec un premier
     * élément vide portant le {@code placeholder} fourni.
     */
    public List<SelectItem> getSelectItems(String placeholder) {
        if (libelleCache == null) charger();
        List<SelectItem> items = new ArrayList<>();
        items.add(new SelectItem("", placeholder));
        for (TypeReferenceResponse t : catalogueCache) {
            if (!Boolean.FALSE.equals(t.getActif())) {
                items.add(new SelectItem(t.getCode(), t.getLibelle()));
            }
        }
        return items;
    }

    /** Retourne tous les types (actifs + inactifs) — usage admin. */
    public List<TypeReferenceResponse> getCatalogueComplet() {
        if (catalogueCache == null) charger();
        return catalogueCache;
    }
}
