package dev.lions.unionflow.client.view;

import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.client.util.LazyDataModelBase;
import dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria;
import dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO;
import dev.lions.unionflow.server.api.dto.membre.response.*;
import org.primefaces.model.FilterMeta;
import org.primefaces.model.SortMeta;
import org.primefaces.model.SortOrder;
import org.jboss.logging.Logger;

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

/**
 * LazyDataModel pour la liste des membres avec pagination, tri et filtrage côté
 * serveur.
 * 
 * <p>
 * Le service REST est passé via le constructeur car cette classe est instanciée
 * manuellement par le bean CDI (pas d'injection CDI possible).
 *
 * @author UnionFlow Team
 * @version 2.0
 */
public class MembreLazyDataModel extends LazyDataModelBase<MembreSummaryResponse> {

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

    private final MembreService membreService;
    private MembreSearchCriteria searchCriteria;
    /** Dernière page chargée, utilisée par getRowData() pour la résolution inverse. */
    private List<MembreSummaryResponse> lastLoadedPage = new ArrayList<>();

    /**
     * Constructeur avec injection du service REST et des critères de recherche.
     *
     * @param membreService  Service REST des membres (injecté par le bean CDI)
     * @param searchCriteria Critères de recherche initiaux
     */
    public MembreLazyDataModel(MembreService membreService, MembreSearchCriteria searchCriteria) {
        this.membreService = membreService;
        this.searchCriteria = searchCriteria != null ? searchCriteria : MembreSearchCriteria.builder().build();
    }

    @Override
    protected List<MembreSummaryResponse> loadData(int first, int pageSize,
            Map<String, SortMeta> sortBy,
            Map<String, FilterMeta> filters) {
        try {
            applyFiltersToCriteria(filters);
            searchCriteria.sanitize();

            // Sans critère, l'endpoint /search/advanced refuse la requête (HTTP 400)
            if (!searchCriteria.hasAnyCriteria()) {
                int pageNum = first / pageSize;
                dev.lions.unionflow.server.api.dto.common.PagedResponse<MembreSummaryResponse> pagedResult =
                        membreService.listerTous(pageNum, pageSize);
                if (pagedResult == null || pagedResult.getData() == null || pagedResult.getData().isEmpty()) {
                    return List.of();
                }
                lastLoadedPage = pagedResult.getData();
                // Mettre à jour le rowCount avec le total réel du backend
                long total = pagedResult.getTotal() != null
                        ? pagedResult.getTotal() : lastLoadedPage.size();
                setRowCount((int) total);
                return lastLoadedPage;
            }

            // Extraire tri
            String sortField = "nom";
            String sortDirection = "asc";
            if (sortBy != null && !sortBy.isEmpty()) {
                SortMeta firstSort = sortBy.values().iterator().next();
                sortField = firstSort.getField() != null ? firstSort.getField() : "nom";
                sortDirection = (firstSort.getOrder() == SortOrder.ASCENDING) ? "asc" : "desc";
            }

            int page = first / pageSize;
            MembreSearchResultDTO result = membreService.rechercherAvance(
                    searchCriteria, page, pageSize, sortField, sortDirection);

            if (result != null && result.getMembres() != null) {
                lastLoadedPage = result.getMembres();
                return lastLoadedPage;
            }
            lastLoadedPage = List.of();
            return List.of();
        } catch (Exception e) {
            LOG.error("Erreur lors du chargement lazy des membres", e);
            lastLoadedPage = List.of();
            return List.of();
        }
    }

    @Override
    public String getRowKey(MembreSummaryResponse object) {
        return object != null && object.getId() != null ? object.getId().toString() : null;
    }

    @Override
    public MembreSummaryResponse getRowData(String rowKey) {
        if (rowKey == null || lastLoadedPage == null) return null;
        for (MembreSummaryResponse m : lastLoadedPage) {
            if (rowKey.equals(getRowKey(m))) return m;
        }
        return null;
    }

    @Override
    protected int countData(Map<String, FilterMeta> filters) {
        try {
            applyFiltersToCriteria(filters);
            searchCriteria.sanitize();

            // Sans critère, compter via listerTous() (page 0, taille 1 pour avoir le total)
            if (!searchCriteria.hasAnyCriteria()) {
                dev.lions.unionflow.server.api.dto.common.PagedResponse<MembreSummaryResponse> r =
                        membreService.listerTous(0, 1);
                if (r == null) return 0;
                return r.getTotal() != null ? r.getTotal().intValue() : 0;
            }

            MembreSearchResultDTO result = membreService.rechercherAvance(
                    searchCriteria, 0, 1, "nom", "asc");

            return result != null ? (int) result.getTotalElements() : 0;
        } catch (Exception e) {
            LOG.error("Erreur lors du comptage des membres", e);
            return 0;
        }
    }

    /**
     * Applique les filtres PrimeFaces (colonnes filtrables) au critère de
     * recherche.
     */
    private void applyFiltersToCriteria(Map<String, FilterMeta> filters) {
        if (filters == null || filters.isEmpty()) {
            return;
        }

        for (Map.Entry<String, FilterMeta> entry : filters.entrySet()) {
            String field = entry.getKey();
            String value = getFilterValueAsString(entry.getValue());

            if (value == null || value.trim().isEmpty()) {
                continue;
            }

            switch (field) {
                case "nom" -> searchCriteria.setNom(value);
                case "prenom" -> searchCriteria.setPrenom(value);
                case "email" -> searchCriteria.setEmail(value);
                case "telephone" -> searchCriteria.setTelephone(value);
                case "statut" -> searchCriteria.setStatut(value);
                case "ville" -> searchCriteria.setVille(value);
                case "profession" -> searchCriteria.setProfession(value);
                default -> LOG.debugf("Filtre non mappé: %s", field);
            }
        }
    }

    // Getters/Setters
    public void setSearchCriteria(MembreSearchCriteria searchCriteria) {
        this.searchCriteria = searchCriteria;
    }

    public MembreSearchCriteria getSearchCriteria() {
        return searchCriteria;
    }
}
