package dev.lions.unionflow.client.util;

import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortMeta;
import org.primefaces.model.SortOrder;
import org.primefaces.model.FilterMeta;
import org.jboss.logging.Logger;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Classe de base pour les LazyDataModel PrimeFaces.
 * 
 * <p>Cette classe abstraite simplifie l'implémentation de LazyDataModel
 * en fournissant des méthodes utilitaires pour la pagination, le tri et le filtrage.
 * 
 * <p>Les classes dérivées doivent implémenter:
 * <ul>
 *   <li>{@link #loadData(int, int, Map, Map)} - Charger les données depuis le backend</li>
 *   <li>{@link #countData(Map)} - Compter le nombre total d'éléments</li>
 * </ul>
 *
 * @param <T> Le type d'entité géré par ce modèle
 * @author UnionFlow Team
 * @version 1.0
 */
public abstract class LazyDataModelBase<T> extends LazyDataModel<T> {

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

    /**
     * Charge les données depuis le backend avec pagination, tri et filtrage.
     * 
     * @param first Le premier index (0-based)
     * @param pageSize La taille de la page
     * @param sortBy Map des critères de tri (field -> SortMeta)
     * @param filters Map des filtres à appliquer (field -> FilterMeta)
     * @return Liste des entités pour la page demandée
     */
    protected abstract List<T> loadData(int first, int pageSize, 
                                       Map<String, SortMeta> sortBy, 
                                       Map<String, FilterMeta> filters);

    /**
     * Compte le nombre total d'éléments correspondant aux filtres.
     * 
     * @param filters Map des filtres à appliquer
     * @return Nombre total d'éléments
     */
    protected abstract int countData(Map<String, FilterMeta> filters);

    @Override
    public List<T> load(int first, int pageSize, 
                       Map<String, SortMeta> sortBy, 
                       Map<String, FilterMeta> filters) {
        try {
            LOG.debugf("Chargement données: first=%d, pageSize=%d, sortBy=%d, filters=%d", 
                      first, pageSize, sortBy != null ? sortBy.size() : 0, 
                      filters != null ? filters.size() : 0);

            // Charger les données
            List<T> data = loadData(first, pageSize, sortBy, filters);
            
            // Compter le total
            int totalCount = countData(filters);
            setRowCount(totalCount);

            LOG.debugf("Données chargées: %d éléments sur %d total", data.size(), totalCount);
            
            return data;
        } catch (Exception e) {
            LOG.error("Erreur lors du chargement des données", e);
            setRowCount(0);
            return List.of();
        }
    }

    @Override
    public int count(Map<String, FilterMeta> filters) {
        return countData(filters);
    }

    /**
     * Convertit une SortMeta en String pour l'API backend.
     * 
     * @param sortMeta Critère de tri
     * @return String au format "field:ASC" ou "field:DESC"
     */
    protected String sortMetaToString(SortMeta sortMeta) {
        if (sortMeta == null) {
            return null;
        }
        
        String field = sortMeta.getField();
        SortOrder order = sortMeta.getOrder();
        String direction = (order == SortOrder.ASCENDING) ? "ASC" : "DESC";
        
        return field + ":" + direction;
    }

    /**
     * Convertit une Map de SortMeta en liste de Strings.
     * 
     * @param sortBy Map des critères de tri (field -> SortMeta)
     * @return Liste de Strings au format "field:ASC" ou "field:DESC"
     */
    protected List<String> sortMetaMapToStringList(Map<String, SortMeta> sortBy) {
        if (sortBy == null || sortBy.isEmpty()) {
            return List.of();
        }
        
        return sortBy.values().stream()
                .map(this::sortMetaToString)
                .collect(Collectors.toList());
    }

    /**
     * Extrait la valeur d'un filtre.
     * 
     * @param filterMeta Métadonnées du filtre
     * @return Valeur du filtre ou null
     */
    protected Object getFilterValue(FilterMeta filterMeta) {
        if (filterMeta == null) {
            return null;
        }
        return filterMeta.getFilterValue();
    }

    /**
     * Extrait la valeur d'un filtre comme String.
     * 
     * @param filterMeta Métadonnées du filtre
     * @return Valeur du filtre comme String ou null
     */
    protected String getFilterValueAsString(FilterMeta filterMeta) {
        Object value = getFilterValue(filterMeta);
        return value != null ? value.toString() : null;
    }
}

