package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Cotisation;
import io.quarkus.arc.Unremovable;
import io.quarkus.panache.common.Page;

import io.quarkus.panache.common.Sort;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.TypedQuery;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

/**
 * Repository pour la gestion des cotisations avec UUID
 *
 * @author UnionFlow Team
 * @version 2.0
 * @since 2025-01-16
 */
@ApplicationScoped
@Unremovable
public class CotisationRepository extends BaseRepository<Cotisation> {

    public CotisationRepository() {
        super(Cotisation.class);
    }

    /**
     * Trouve une cotisation par son numéro de référence
     *
     * @param numeroReference le numéro de référence unique
     * @return Optional contenant la cotisation si trouvée
     */
    public Optional<Cotisation> findByNumeroReference(String numeroReference) {
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.numeroReference = :numeroReference",
                Cotisation.class);
        query.setParameter("numeroReference", numeroReference);
        return query.getResultList().stream().findFirst();
    }

    /**
     * Trouve toutes les cotisations d'un membre
     *
     * @param membreId l'UUID du membre
     * @param page     pagination
     * @param sort     tri
     * @return liste paginée des cotisations
     */
    public List<Cotisation> findByMembreId(UUID membreId, Page page, Sort sort) {
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : "";
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.membre.id = :membreId" + orderBy,
                Cotisation.class);
        query.setParameter("membreId", membreId);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Compte les cotisations d'un membre (pour dashboard). */
    public long countByMembreId(UUID membreId) {
        if (membreId == null) return 0L;
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c WHERE c.membre.id = :membreId", Long.class);
        query.setParameter("membreId", membreId);
        Long result = query.getSingleResult();
        return result != null ? result : 0L;
    }

    /** Compte les cotisations payées d'un membre (statut PAYEE, pour dashboard). */
    public long countPayeesByMembreId(UUID membreId) {
        if (membreId == null) return 0L;
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c WHERE c.membre.id = :membreId AND (c.statut = 'PAYEE' OR c.statut = 'PARTIELLEMENT_PAYEE')",
                Long.class);
        query.setParameter("membreId", membreId);
        Long result = query.getSingleResult();
        return result != null ? result : 0L;
    }

    /**
     * Trouve les cotisations dont l'organisation fait partie de la liste (pour admin / admin org).
     *
     * @param organisationIds ensemble des UUID d'organisations
     * @param page            pagination
     * @param sort            tri
     * @return liste paginée des cotisations
     */
    public List<Cotisation> findByOrganisationIdIn(Set<UUID> organisationIds, Page page, Sort sort) {
        if (organisationIds == null || organisationIds.isEmpty()) {
            return List.of();
        }
        String orderBy = sort != null ? " ORDER BY " + buildOrderBy(sort) : " ORDER BY c.dateEcheance DESC";
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.organisation.id IN :organisationIds" + orderBy,
                Cotisation.class);
        query.setParameter("organisationIds", organisationIds);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /** Compte les cotisations d'une organisation (pour dashboard par org). */
    public long countByOrganisationId(UUID organisationId) {
        if (organisationId == null) return 0L;
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c WHERE c.organisation.id = :organisationId", Long.class);
        query.setParameter("organisationId", organisationId);
        Long result = query.getSingleResult();
        return result != null ? result : 0L;
    }

    /** Compte les cotisations d'une organisation avec date de paiement dans une période (pour graphiques). */
    public long countByOrganisationIdAndDatePaiementBetween(UUID organisationId, java.time.LocalDateTime start, java.time.LocalDateTime end) {
        if (organisationId == null) return 0L;
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c WHERE c.organisation.id = :organisationId AND c.datePaiement >= :start AND c.datePaiement <= :end",
                Long.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("start", start);
        query.setParameter("end", end);
        Long result = query.getSingleResult();
        return result != null ? result : 0L;
    }

    /**
     * Trouve les cotisations en attente pour les organisations données (pour admin / admin org).
     *
     * @param organisationIds ensemble des UUID d'organisations
     * @return liste des cotisations en attente, triées par date d'échéance
     */
    public List<Cotisation> findEnAttenteByOrganisationIdIn(Set<UUID> organisationIds) {
        if (organisationIds == null || organisationIds.isEmpty()) {
            return List.of();
        }
        int anneeEnCours = LocalDate.now().getYear();
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.organisation.id IN :organisationIds "
                + "AND c.statut = 'EN_ATTENTE' AND EXTRACT(YEAR FROM c.dateEcheance) = :annee "
                + "ORDER BY c.dateEcheance ASC",
                Cotisation.class);
        query.setParameter("organisationIds", organisationIds);
        query.setParameter("annee", anneeEnCours);
        return query.getResultList();
    }

    /**
     * Trouve les cotisations par statut
     *
     * @param statut le statut recherché
     * @param page   pagination
     * @return liste paginée des cotisations
     */
    public List<Cotisation> findByStatut(String statut, Page page) {
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.statut = :statut ORDER BY c.dateEcheance DESC",
                Cotisation.class);
        query.setParameter("statut", statut);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Trouve les cotisations en retard
     *
     * @param dateReference date de référence (généralement aujourd'hui)
     * @param page          pagination
     * @return liste des cotisations en retard
     */
    public List<Cotisation> findCotisationsEnRetard(LocalDate dateReference, Page page) {
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.dateEcheance < :dateReference AND c.statut != 'PAYEE' AND c.statut != 'ANNULEE' ORDER BY c.dateEcheance ASC",
                Cotisation.class);
        query.setParameter("dateReference", dateReference);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Trouve les cotisations par période (année/mois)
     *
     * @param annee l'année
     * @param mois  le mois (optionnel)
     * @param page  pagination
     * @return liste des cotisations de la période
     */
    public List<Cotisation> findByPeriode(Integer annee, Integer mois, Page page) {
        TypedQuery<Cotisation> query;
        if (mois != null) {
            query = entityManager.createQuery(
                    "SELECT c FROM Cotisation c WHERE c.annee = :annee AND c.mois = :mois ORDER BY c.dateEcheance DESC",
                    Cotisation.class);
            query.setParameter("annee", annee);
            query.setParameter("mois", mois);
        } else {
            query = entityManager.createQuery(
                    "SELECT c FROM Cotisation c WHERE c.annee = :annee ORDER BY c.mois DESC, c.dateEcheance DESC",
                    Cotisation.class);
            query.setParameter("annee", annee);
        }
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Trouve les cotisations par type
     *
     * @param typeCotisation le type de cotisation
     * @param page           pagination
     * @return liste des cotisations du type spécifié
     */
    public List<Cotisation> findByType(String typeCotisation, Page page) {
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.typeCotisation = :typeCotisation ORDER BY c.dateEcheance DESC",
                Cotisation.class);
        query.setParameter("typeCotisation", typeCotisation);
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Recherche avancée avec filtres multiples
     *
     * @param membreId       UUID du membre (optionnel)
     * @param statut         Statut de la cotisation (optionnel)
     * @param typeCotisation Type de cotisation (optionnel)
     * @param annee          Année (optionnel)
     * @param page           Pagination
     * @param sort           Tri
     * @return liste paginée des cotisations filtrées
     */
    public List<Cotisation> searchAdvanced(UUID membreId, String statut, String typeCotisation, Integer annee,
            Page page, Sort sort) {
        StringBuilder queryStr = new StringBuilder("SELECT c FROM Cotisation c WHERE 1=1 ");
        Map<String, Object> params = new java.util.HashMap<>();

        if (membreId != null) {
            queryStr.append("AND c.membre.id = :membreId ");
            params.put("membreId", membreId);
        }
        if (statut != null && !statut.isBlank()) {
            queryStr.append("AND c.statut = :statut ");
            params.put("statut", statut);
        }
        if (typeCotisation != null && !typeCotisation.isBlank()) {
            queryStr.append("AND c.typeCotisation = :typeCotisation ");
            params.put("typeCotisation", typeCotisation);
        }
        if (annee != null) {
            queryStr.append("AND c.annee = :annee ");
            params.put("annee", annee);
        }

        if (sort != null) {
            queryStr.append(" ORDER BY ").append(buildOrderBy(sort));
        } else {
            queryStr.append(" ORDER BY c.dateEcheance DESC");
        }

        TypedQuery<Cotisation> query = entityManager.createQuery(queryStr.toString(), Cotisation.class);
        params.forEach(query::setParameter);

        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);

        return query.getResultList();
    }

    // --- Méthodes d'agrégation pour le dashboard membre --- //

    public BigDecimal calculerTotalCotisationsPayeesCeMois(UUID membreId) {
        LocalDate startOfMonth = LocalDate.now().withDayOfMonth(1);
        LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth());

        BigDecimal total = entityManager.createQuery(
                "SELECT SUM(c.montantPaye) FROM Cotisation c WHERE c.membre.id = :membreId AND c.statut = 'PAYEE' AND c.datePaiement >= :start AND c.datePaiement <= :end",
                BigDecimal.class)
                .setParameter("membreId", membreId)
                .setParameter("start", startOfMonth.atStartOfDay())
                .setParameter("end", endOfMonth.atTime(23, 59, 59))
                .getSingleResult();
        return total != null ? total : BigDecimal.ZERO;
    }

    public long countRetardByMembreId(UUID membreId) {
        Long count = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c WHERE c.membre.id = :membreId AND c.dateEcheance < :today AND c.statut != 'PAYEE' AND c.statut != 'ANNULEE'",
                Long.class)
                .setParameter("membreId", membreId)
                .setParameter("today", LocalDate.now())
                .getSingleResult();
        return count != null ? count : 0L;
    }

    public BigDecimal calculerTotalCotisationsAnneeEnCours(UUID membreId) {
        int anneeCourante = LocalDate.now().getYear();
        BigDecimal total = entityManager.createQuery(
                "SELECT SUM(c.montantDu) FROM Cotisation c WHERE c.membre.id = :membreId AND c.annee = :annee",
                BigDecimal.class)
                .setParameter("membreId", membreId)
                .setParameter("annee", anneeCourante)
                .getSingleResult();
        return total != null ? total : BigDecimal.ZERO;
    }

    public BigDecimal calculerTotalCotisationsPayeesAnneeEnCours(UUID membreId) {
        int anneeCourante = LocalDate.now().getYear();
        BigDecimal total = entityManager.createQuery(
                "SELECT SUM(c.montantPaye) FROM Cotisation c WHERE c.membre.id = :membreId AND c.annee = :annee AND (c.statut = 'PAYEE' OR c.statut = 'PARTIELLEMENT_PAYEE')",
                BigDecimal.class)
                .setParameter("membreId", membreId)
                .setParameter("annee", anneeCourante)
                .getSingleResult();
        return total != null ? total : BigDecimal.ZERO;
    }

    /** Total des cotisations payées par le membre (toutes années) pour le KPI « Contribution Totale ». */
    public BigDecimal calculerTotalCotisationsPayeesToutTemps(UUID membreId) {
        BigDecimal total = entityManager.createQuery(
                "SELECT SUM(c.montantPaye) FROM Cotisation c WHERE c.membre.id = :membreId AND (c.statut = 'PAYEE' OR c.statut = 'PARTIELLEMENT_PAYEE')",
                BigDecimal.class)
                .setParameter("membreId", membreId)
                .getSingleResult();
        return total != null ? total : BigDecimal.ZERO;
    }

    /**
     * Recherche avancee
     */

    public List<Cotisation> rechercheAvancee(
            UUID membreId, String statut, String typeCotisation, Integer annee, Integer mois, Page page) {
        StringBuilder jpql = new StringBuilder("SELECT c FROM Cotisation c WHERE 1=1");
        Map<String, Object> params = new HashMap<>();

        if (membreId != null) {
            jpql.append(" AND c.membre.id = :membreId");
            params.put("membreId", membreId);
        }

        if (statut != null && !statut.isEmpty()) {
            jpql.append(" AND c.statut = :statut");
            params.put("statut", statut);
        }

        if (typeCotisation != null && !typeCotisation.isEmpty()) {
            jpql.append(" AND c.typeCotisation = :typeCotisation");
            params.put("typeCotisation", typeCotisation);
        }

        if (annee != null) {
            jpql.append(" AND c.annee = :annee");
            params.put("annee", annee);
        }

        if (mois != null) {
            jpql.append(" AND c.mois = :mois");
            params.put("mois", mois);
        }

        jpql.append(" ORDER BY c.dateEcheance DESC");

        TypedQuery<Cotisation> query = entityManager.createQuery(jpql.toString(), Cotisation.class);
        for (Map.Entry<String, Object> param : params.entrySet()) {
            query.setParameter(param.getKey(), param.getValue());
        }
        query.setFirstResult(page.index * page.size);
        query.setMaxResults(page.size);
        return query.getResultList();
    }

    /**
     * Calcule le total des montants dus pour un membre
     *
     * @param membreId UUID du membre
     * @return montant total dû
     */
    public BigDecimal calculerTotalMontantDu(UUID membreId) {
        TypedQuery<BigDecimal> query = entityManager.createQuery(
                "SELECT COALESCE(SUM(c.montantDu), 0) FROM Cotisation c WHERE c.membre.id = :membreId",
                BigDecimal.class);
        query.setParameter("membreId", membreId);
        BigDecimal result = query.getSingleResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /**
     * Calcule le total des montants payés pour un membre
     *
     * @param membreId UUID du membre
     * @return montant total payé
     */
    public BigDecimal calculerTotalMontantPaye(UUID membreId) {
        TypedQuery<BigDecimal> query = entityManager.createQuery(
                "SELECT COALESCE(SUM(c.montantPaye), 0) FROM Cotisation c WHERE c.membre.id = :membreId",
                BigDecimal.class);
        query.setParameter("membreId", membreId);
        BigDecimal result = query.getSingleResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /**
     * Compte les cotisations par statut
     *
     * @param statut le statut
     * @return nombre de cotisations
     */
    public long compterParStatut(String statut) {
        TypedQuery<Long> query = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c WHERE c.statut = :statut", Long.class);
        query.setParameter("statut", statut);
        return query.getSingleResult();
    }

    /**
     * Somme des montants payés pour un statut donné (ex. PAYEE pour revenus
     * perçus).
     */
    public BigDecimal sommeMontantPayeParStatut(String statut) {
        TypedQuery<BigDecimal> query = entityManager.createQuery(
                "SELECT COALESCE(SUM(c.montantPaye), 0) FROM Cotisation c WHERE c.statut = :statut",
                BigDecimal.class);
        query.setParameter("statut", statut);
        BigDecimal result = query.getSingleResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /**
     * Somme de tous les montants dus.
     */
    public BigDecimal sommeMontantDu() {
        TypedQuery<BigDecimal> query = entityManager.createQuery(
                "SELECT COALESCE(SUM(c.montantDu), 0) FROM Cotisation c",
                BigDecimal.class);
        return query.getSingleResult();
    }

    /**
     * Trouve les cotisations nécessitant un rappel
     *
     * @param joursAvantEcheance nombre de jours avant échéance
     * @param nombreMaxRappels   nombre maximum de rappels déjà envoyés
     * @return liste des cotisations à rappeler
     */
    public List<Cotisation> findCotisationsAuRappel(int joursAvantEcheance, int nombreMaxRappels) {
        LocalDate dateRappel = LocalDate.now().plusDays(joursAvantEcheance);
        TypedQuery<Cotisation> query = entityManager.createQuery(
                "SELECT c FROM Cotisation c WHERE c.dateEcheance <= :dateRappel AND c.statut != 'PAYEE' AND c.statut != 'ANNULEE' AND c.nombreRappels < :nombreMaxRappels ORDER BY c.dateEcheance ASC",
                Cotisation.class);
        query.setParameter("dateRappel", dateRappel);
        query.setParameter("nombreMaxRappels", nombreMaxRappels);
        return query.getResultList();
    }

    /**
     * Met à jour le nombre de rappels pour une cotisation
     *
     * @param cotisationId UUID de la cotisation
     * @return true si mise à jour réussie
     */
    public boolean incrementerNombreRappels(UUID cotisationId) {
        Cotisation cotisation = findByIdOptional(cotisationId).orElse(null);
        if (cotisation != null) {
            cotisation.setNombreRappels(cotisation.getNombreRappels() + 1);
            cotisation.setDateDernierRappel(LocalDateTime.now());
            update(cotisation);
            return true;
        }
        return false;
    }

    /**
     * Statistiques des cotisations par période
     *
     * @param annee l'année
     * @param mois  le mois (optionnel)
     * @return map avec les statistiques
     */
    public Map<String, Object> getStatistiquesPeriode(Integer annee, Integer mois) {
        String baseQuery = mois != null
                ? "SELECT c FROM Cotisation c WHERE c.annee = :annee AND c.mois = :mois"
                : "SELECT c FROM Cotisation c WHERE c.annee = :annee";

        TypedQuery<Long> countQuery;
        TypedQuery<BigDecimal> montantTotalQuery;
        TypedQuery<BigDecimal> montantPayeQuery;
        TypedQuery<Long> payeesQuery;

        if (mois != null) {
            countQuery = entityManager.createQuery(
                    "SELECT COUNT(c) FROM Cotisation c WHERE c.annee = :annee AND c.mois = :mois",
                    Long.class);
            montantTotalQuery = entityManager.createQuery(
                    "SELECT COALESCE(SUM(c.montantDu), 0) FROM Cotisation c WHERE c.annee = :annee AND c.mois = :mois",
                    BigDecimal.class);
            montantPayeQuery = entityManager.createQuery(
                    "SELECT COALESCE(SUM(c.montantPaye), 0) FROM Cotisation c WHERE c.annee = :annee AND c.mois = :mois",
                    BigDecimal.class);
            payeesQuery = entityManager.createQuery(
                    "SELECT COUNT(c) FROM Cotisation c WHERE c.annee = :annee AND c.mois = :mois AND c.statut = 'PAYEE'",
                    Long.class);

            countQuery.setParameter("annee", annee);
            countQuery.setParameter("mois", mois);
            montantTotalQuery.setParameter("annee", annee);
            montantTotalQuery.setParameter("mois", mois);
            montantPayeQuery.setParameter("annee", annee);
            montantPayeQuery.setParameter("mois", mois);
            payeesQuery.setParameter("annee", annee);
            payeesQuery.setParameter("mois", mois);
        } else {
            countQuery = entityManager.createQuery(
                    "SELECT COUNT(c) FROM Cotisation c WHERE c.annee = :annee", Long.class);
            montantTotalQuery = entityManager.createQuery(
                    "SELECT COALESCE(SUM(c.montantDu), 0) FROM Cotisation c WHERE c.annee = :annee",
                    BigDecimal.class);
            montantPayeQuery = entityManager.createQuery(
                    "SELECT COALESCE(SUM(c.montantPaye), 0) FROM Cotisation c WHERE c.annee = :annee",
                    BigDecimal.class);
            payeesQuery = entityManager.createQuery(
                    "SELECT COUNT(c) FROM Cotisation c WHERE c.annee = :annee AND c.statut = 'PAYEE'",
                    Long.class);

            countQuery.setParameter("annee", annee);
            montantTotalQuery.setParameter("annee", annee);
            montantPayeQuery.setParameter("annee", annee);
            payeesQuery.setParameter("annee", annee);
        }

        Long totalCotisations = countQuery.getSingleResult();
        BigDecimal montantTotal = montantTotalQuery.getSingleResult();
        BigDecimal montantPaye = montantPayeQuery.getSingleResult();
        Long cotisationsPayees = payeesQuery.getSingleResult();

        return Map.of(
                "totalCotisations", totalCotisations != null ? totalCotisations : 0L,
                "montantTotal", montantTotal != null ? montantTotal : BigDecimal.ZERO,
                "montantPaye", montantPaye != null ? montantPaye : BigDecimal.ZERO,
                "cotisationsPayees", cotisationsPayees != null ? cotisationsPayees : 0L,
                "tauxPaiement",
                totalCotisations != null && totalCotisations > 0
                        ? cotisationsPayees * 100.0 / totalCotisations
                        : 0.0);
    }

    /** Somme des montants payés dans une période */
    public BigDecimal sumMontantsPayes(
            UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
        TypedQuery<BigDecimal> query = entityManager.createQuery(
                "SELECT COALESCE(SUM(c.montantPaye), 0) FROM Cotisation c WHERE c.organisation.id = :organisationId AND c.statut = 'PAYEE' AND c.datePaiement BETWEEN :debut AND :fin",
                BigDecimal.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("debut", debut);
        query.setParameter("fin", fin);
        BigDecimal result = query.getSingleResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /** Somme des montants en attente dans une période */
    public BigDecimal sumMontantsEnAttente(
            UUID organisationId, LocalDateTime debut, LocalDateTime fin) {
        TypedQuery<BigDecimal> query = entityManager.createQuery(
                "SELECT COALESCE(SUM(c.montantDu), 0) FROM Cotisation c WHERE c.organisation.id = :organisationId AND c.statut = 'EN_ATTENTE' AND c.dateCreation BETWEEN :debut AND :fin",
                BigDecimal.class);
        query.setParameter("organisationId", organisationId);
        query.setParameter("debut", debut);
        query.setParameter("fin", fin);
        BigDecimal result = query.getSingleResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    /** Construit la clause ORDER BY à partir d'un Sort */
    private String buildOrderBy(Sort sort) {
        if (sort == null || sort.getColumns().isEmpty()) {
            return "c.dateEcheance DESC";
        }
        StringBuilder orderBy = new StringBuilder();
        for (int i = 0; i < sort.getColumns().size(); i++) {
            if (i > 0) {
                orderBy.append(", ");
            }
            Sort.Column column = sort.getColumns().get(i);
            orderBy.append("c.").append(column.getName());
            if (column.getDirection() == Sort.Direction.Descending) {
                orderBy.append(" DESC");
            } else {
                orderBy.append(" ASC");
            }
        }
        return orderBy.toString();
    }

    /**
     * Vérifie si une cotisation mensuelle existe déjà pour ce membre, cette organisation,
     * cette année et ce mois. Utilisé pour éviter les doublons lors de la génération automatique.
     */
    public boolean existsByMembreOrganisationAnneeAndMois(UUID membreId, UUID organisationId, int annee, int mois) {
        Long count = entityManager.createQuery(
                "SELECT COUNT(c) FROM Cotisation c "
                + "WHERE c.membre.id = :membreId AND c.organisation.id = :orgId "
                + "AND c.annee = :annee AND c.mois = :mois",
                Long.class)
            .setParameter("membreId", membreId)
            .setParameter("orgId", organisationId)
            .setParameter("annee", annee)
            .setParameter("mois", mois)
            .getSingleResult();
        return count != null && count > 0;
    }
}
