package dev.lions.unionflow.client.view;

import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import dev.lions.unionflow.client.dto.CotisationDTO;
import dev.lions.unionflow.client.service.CotisationService;
import dev.lions.unionflow.client.service.NotificationClientService;
import dev.lions.unionflow.client.service.ExportClientService;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;

/**
 * Bean JSF pour la gestion des cotisations
 * Refactorisé pour utiliser directement CotisationDTO et se connecter au backend
 * 
 * @author UnionFlow Team
 * @version 2.0
 */
@Named("cotisationsBean")
@SessionScoped
public class CotisationsBean implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = Logger.getLogger(CotisationsBean.class.getName());
    
    @Inject
    @RestClient
    private CotisationService cotisationService;
    
    @Inject
    @RestClient
    private NotificationClientService notificationService;
    
    @Inject
    @RestClient
    private ExportClientService exportService;
    
    // Données principales - Utilisation directe de CotisationDTO
    private List<CotisationDTO> toutesLesCotisations;
    private List<CotisationDTO> cotisationsFiltrees;
    private List<CotisationDTO> cotisationsSelectionnees;
    private CotisationDTO cotisationSelectionnee;
    
    // Formulaire nouvelle cotisation
    private NouvelleCotisation nouvelleCotisation;
    
    // Filtres
    private Filtres filtres;
    
    // Statistiques
    private StatistiquesFinancieres statistiques;
    
    // Analytics
    private List<EvolutionPaiement> evolutionPaiements;
    private List<RepartitionMethode> repartitionMethodes;
    private List<RappelCotisation> rappelsEnAttente;
    
    @PostConstruct
    public void init() {
        initializeFiltres();
        chargerCotisations();
        chargerStatistiques();
        initializeNouvelleCotisation();
        chargerEvolutionPaiements();
        chargerRepartitionMethodes();
        chargerRappels();
        appliquerFiltres();
    }
    
    private void initializeFiltres() {
        filtres = new Filtres();
        cotisationsSelectionnees = new ArrayList<>();
    }
    
    /**
     * Charge les cotisations depuis le backend
     */
    private void chargerCotisations() {
        toutesLesCotisations = new ArrayList<>();
        try {
            toutesLesCotisations = cotisationService.listerToutes(0, 1000);
            LOGGER.info("Chargement de " + toutesLesCotisations.size() + " cotisations");
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des cotisations: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de charger les cotisations: " + e.getMessage()));
        }
    }
    
    /**
     * Charge les statistiques depuis le backend
     */
    private void chargerStatistiques() {
        statistiques = new StatistiquesFinancieres();
        try {
            Map<String, Object> statsBackend = cotisationService.obtenirStatistiques();
            
            // Extraction des statistiques du backend
            Long cotisationsEnRetard = ((Number) statsBackend.getOrDefault("cotisationsEnRetard", 0L)).longValue();
            Double tauxPaiement = ((Number) statsBackend.getOrDefault("tauxPaiement", 0.0)).doubleValue();
            
            // Calcul des montants depuis les cotisations réelles
            BigDecimal totalCollecte = toutesLesCotisations.stream()
                .filter(c -> "PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut()))
                .map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            BigDecimal montantRetard = toutesLesCotisations.stream()
                .filter(c -> "EN_RETARD".equals(c.getStatut()))
                .map(c -> c.getMontantDu() != null ? c.getMontantDu() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            statistiques.setTotalCollecte(totalCollecte);
            statistiques.setObjectifAnnuel(totalCollecte.multiply(new BigDecimal("1.3")));
            statistiques.setTauxRecouvrement(tauxPaiement);
            statistiques.setCotisationsEnRetard(cotisationsEnRetard.intValue());
            statistiques.setMontantRetard(montantRetard);
            
            // Moyenne mensuelle basée sur les 12 derniers mois
            BigDecimal moyenneMensuelle = totalCollecte.divide(new BigDecimal("12"), 2, java.math.RoundingMode.HALF_UP);
            statistiques.setMoyenneMensuelle(moyenneMensuelle);
            
            LOGGER.info("Statistiques chargées: Total=" + totalCollecte + ", Taux=" + tauxPaiement + "%");
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des statistiques: " + e.getMessage());
            statistiques.setTotalCollecte(BigDecimal.ZERO);
            statistiques.setObjectifAnnuel(BigDecimal.ZERO);
            statistiques.setTauxRecouvrement(0.0);
            statistiques.setCotisationsEnRetard(0);
            statistiques.setMontantRetard(BigDecimal.ZERO);
            statistiques.setMoyenneMensuelle(BigDecimal.ZERO);
        }
    }
    
    /**
     * Calcule l'évolution des paiements depuis les données réelles
     */
    private void chargerEvolutionPaiements() {
        evolutionPaiements = new ArrayList<>();
        try {
            // Récupérer les cotisations payées des 12 derniers mois
            LocalDate maintenant = LocalDate.now();
            String[] moisNoms = {"Jan", "Fév", "Mar", "Avr", "Mai", "Jun", "Jul", "Aoû", "Sep", "Oct", "Nov", "Déc"};
            
            for (int i = 11; i >= 0; i--) {
                LocalDate moisDate = maintenant.minusMonths(i);
                Month mois = moisDate.getMonth();
                int annee = moisDate.getYear();
                
                BigDecimal montantMois = toutesLesCotisations.stream()
                    .filter(c -> c.getDatePaiement() != null 
                        && c.getDatePaiement().getYear() == annee
                        && c.getDatePaiement().getMonth() == mois
                        && ("PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut())))
                    .map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                
                EvolutionPaiement evolution = new EvolutionPaiement();
                evolution.setMois(moisNoms[mois.getValue() - 1]);
                evolution.setMontant(montantMois);
                evolution.setHauteur(montantMois.compareTo(BigDecimal.ZERO) > 0 
                    ? (int) (montantMois.divide(new BigDecimal("50000"), 0, java.math.RoundingMode.HALF_UP).intValue())
                    : 0);
                evolutionPaiements.add(evolution);
            }
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du calcul de l'évolution des paiements: " + e.getMessage());
        }
    }
    
    /**
     * Calcule la répartition par méthode de paiement depuis les données réelles
     */
    private void chargerRepartitionMethodes() {
        repartitionMethodes = new ArrayList<>();
        try {
            // Calculer le total des paiements
            BigDecimal totalPaiements = toutesLesCotisations.stream()
                .filter(c -> c.getMethodePaiement() != null 
                    && ("PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut())))
                .map(c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            if (totalPaiements.compareTo(BigDecimal.ZERO) == 0) {
                return; // Pas de paiements
            }
            
            // Grouper par méthode de paiement
            Map<String, BigDecimal> parMethode = toutesLesCotisations.stream()
                .filter(c -> c.getMethodePaiement() != null 
                    && ("PAYEE".equals(c.getStatut()) || "PARTIELLEMENT_PAYEE".equals(c.getStatut())))
                .collect(Collectors.groupingBy(
                    CotisationDTO::getMethodePaiement,
                    Collectors.reducing(BigDecimal.ZERO,
                        c -> c.getMontantPaye() != null ? c.getMontantPaye() : BigDecimal.ZERO,
                        BigDecimal::add)));
            
            // Créer les objets RepartitionMethode
            for (Map.Entry<String, BigDecimal> entry : parMethode.entrySet()) {
                String methode = entry.getKey();
                BigDecimal montant = entry.getValue();
                double pourcentage = montant.multiply(BigDecimal.valueOf(100))
                    .divide(totalPaiements, 2, java.math.RoundingMode.HALF_UP)
                    .doubleValue();
                
                RepartitionMethode repartition = new RepartitionMethode();
                repartition.setMethode(getMethodeLibelle(methode));
                repartition.setMontant(montant);
                repartition.setPourcentage(pourcentage);
                repartition.setCouleur(getCouleurMethode(methode));
                repartition.setIcon(getIconMethode(methode));
                repartitionMethodes.add(repartition);
            }
            
            // Trier par montant décroissant
            repartitionMethodes.sort((a, b) -> b.getMontant().compareTo(a.getMontant()));
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du calcul de la répartition des méthodes: " + e.getMessage());
        }
    }
    
    /**
     * Charge les rappels depuis les cotisations en retard
     */
    private void chargerRappels() {
        rappelsEnAttente = new ArrayList<>();
        try {
            List<CotisationDTO> enRetard = cotisationService.obtenirEnRetard(0, 100);
            
            for (CotisationDTO cotisation : enRetard) {
                RappelCotisation rappel = new RappelCotisation();
                rappel.setNomMembre(cotisation.getNomMembre());
                rappel.setClub(cotisation.getNomAssociation());
                rappel.setMontantDu(cotisation.getMontantDu());
                rappel.setJoursRetard((int) cotisation.getJoursRetard());
                rappel.setPriorite(determinerPriorite(cotisation.getJoursRetard()));
                rappelsEnAttente.add(rappel);
            }
            
            // Trier par priorité (HAUTE > MOYENNE > FAIBLE)
            rappelsEnAttente.sort((a, b) -> {
                int prioriteA = getPrioriteValue(a.getPriorite());
                int prioriteB = getPrioriteValue(b.getPriorite());
                if (prioriteA != prioriteB) return prioriteB - prioriteA;
                return b.getJoursRetard() - a.getJoursRetard();
            });
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des rappels: " + e.getMessage());
        }
    }
    
    private String determinerPriorite(long joursRetard) {
        if (joursRetard >= 30) return "HAUTE";
        if (joursRetard >= 15) return "MOYENNE";
        return "FAIBLE";
    }
    
    private int getPrioriteValue(String priorite) {
        return switch (priorite) {
            case "HAUTE" -> 3;
            case "MOYENNE" -> 2;
            case "FAIBLE" -> 1;
            default -> 0;
        };
    }
    
    private String getMethodeLibelle(String methode) {
        if (methode == null) return "Non défini";
        return switch (methode) {
            case "WAVE_MONEY" -> "Wave Money";
            case "ESPECES" -> "Espèces";
            case "VIREMENT" -> "Virement";
            case "CHEQUE" -> "Chèque";
            case "ORANGE_MONEY" -> "Orange Money";
            case "FREE_MONEY" -> "Free Money";
            case "CARTE_BANCAIRE" -> "Carte bancaire";
            default -> methode;
        };
    }
    
    private String getCouleurMethode(String methode) {
        if (methode == null) return "bg-gray-500";
        return switch (methode) {
            case "WAVE_MONEY" -> "bg-blue-500";
            case "ESPECES" -> "bg-green-500";
            case "VIREMENT" -> "bg-purple-500";
            case "CHEQUE" -> "bg-orange-500";
            case "ORANGE_MONEY" -> "bg-orange-400";
            case "FREE_MONEY" -> "bg-yellow-500";
            case "CARTE_BANCAIRE" -> "bg-indigo-500";
            default -> "bg-gray-500";
        };
    }
    
    private String getIconMethode(String methode) {
        if (methode == null) return "pi-circle";
        return switch (methode) {
            case "WAVE_MONEY", "ORANGE_MONEY", "FREE_MONEY" -> "pi-mobile";
            case "ESPECES" -> "pi-money-bill";
            case "VIREMENT" -> "pi-send";
            case "CHEQUE", "CARTE_BANCAIRE" -> "pi-credit-card";
            default -> "pi-circle";
        };
    }
    
    private void initializeNouvelleCotisation() {
        nouvelleCotisation = new NouvelleCotisation();
    }
    
    /**
     * Applique les filtres en utilisant la recherche backend
     */
    private void appliquerFiltres() {
        try {
            // Utiliser la recherche backend au lieu du filtrage côté client
            cotisationsFiltrees = cotisationService.rechercher(
                null, // membreId - peut être ajouté si nécessaire
                filtres.getStatut(),
                filtres.getTypeCotisation(),
                null, // annee
                null, // mois
                0,
                1000
            );
            
            // Appliquer les filtres supplémentaires côté client si nécessaire
            if (filtres.getNomMembre() != null && !filtres.getNomMembre().trim().isEmpty()) {
                cotisationsFiltrees = cotisationsFiltrees.stream()
                    .filter(c -> c.getNomMembre() != null 
                        && c.getNomMembre().toLowerCase().contains(filtres.getNomMembre().toLowerCase()))
                    .collect(Collectors.toList());
            }
            
            if (filtres.getClub() != null && !filtres.getClub().trim().isEmpty()) {
                cotisationsFiltrees = cotisationsFiltrees.stream()
                    .filter(c -> c.getNomAssociation() != null 
                        && c.getNomAssociation().toLowerCase().contains(filtres.getClub().toLowerCase()))
                    .collect(Collectors.toList());
            }
            
            if (filtres.getDateDebut() != null) {
                cotisationsFiltrees = cotisationsFiltrees.stream()
                    .filter(c -> c.getDateEcheance() != null 
                        && !c.getDateEcheance().isBefore(filtres.getDateDebut()))
                    .collect(Collectors.toList());
            }
            
            if (filtres.getDateFin() != null) {
                cotisationsFiltrees = cotisationsFiltrees.stream()
                    .filter(c -> c.getDateEcheance() != null 
                        && !c.getDateEcheance().isAfter(filtres.getDateFin()))
                    .collect(Collectors.toList());
            }
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'application des filtres: " + e.getMessage());
            cotisationsFiltrees = new ArrayList<>();
        }
    }
    
    // Actions
    
    /**
     * Recherche avec filtres
     */
    public void rechercher() {
        appliquerFiltres();
        FacesContext.getCurrentInstance().addMessage(null, 
            new FacesMessage(FacesMessage.SEVERITY_INFO, "Recherche", 
                cotisationsFiltrees.size() + " cotisation(s) trouvée(s)"));
    }
    
    /**
     * Réinitialise les filtres
     */
    public void reinitialiserFiltres() {
        filtres = new Filtres();
        chargerCotisations();
        appliquerFiltres();
    }
    
    /**
     * Enregistre une nouvelle cotisation via le backend
     */
    public void enregistrerCotisation() {
        try {
            CotisationDTO nouvelleCot = new CotisationDTO();
            nouvelleCot.setMembreId(nouvelleCotisation.getMembreId());
            nouvelleCot.setTypeCotisation(nouvelleCotisation.getTypeCotisation());
            nouvelleCot.setLibelle(nouvelleCotisation.getLibelle());
            nouvelleCot.setDescription(nouvelleCotisation.getDescription());
            nouvelleCot.setMontantDu(nouvelleCotisation.getMontantDu());
            nouvelleCot.setDateEcheance(nouvelleCotisation.getDateEcheance());
            nouvelleCot.setStatut("EN_ATTENTE");
            nouvelleCot.setMontantPaye(BigDecimal.ZERO);
            nouvelleCot.setCodeDevise("XOF");
            nouvelleCot.setObservations(nouvelleCotisation.getObservations());
            
            CotisationDTO cotisationCreee = cotisationService.creer(nouvelleCot);
            
            // Recharger les données
            chargerCotisations();
            chargerStatistiques();
            appliquerFiltres();
            initializeNouvelleCotisation();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Cotisation créée avec succès"));
            LOGGER.info("Nouvelle cotisation créée: " + cotisationCreee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la création de la cotisation: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de créer la cotisation: " + e.getMessage()));
        }
    }
    
    /**
     * Marque une cotisation comme payée via le backend
     */
    public void marquerCommePaye() {
        if (cotisationSelectionnee == null) {
            return;
        }
        
        try {
            cotisationSelectionnee.setStatut("PAYEE");
            cotisationSelectionnee.setMontantPaye(cotisationSelectionnee.getMontantDu());
            cotisationSelectionnee.setDatePaiement(LocalDateTime.now());
            
            cotisationService.modifier(cotisationSelectionnee.getId(), cotisationSelectionnee);
            
            // Recharger les données
            chargerCotisations();
            chargerStatistiques();
            appliquerFiltres();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Cotisation marquée comme payée"));
            LOGGER.info("Cotisation marquée comme payée: " + cotisationSelectionnee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du marquage de la cotisation: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de marquer la cotisation comme payée: " + e.getMessage()));
        }
    }
    
    /**
     * Enregistre un paiement partiel via le backend
     */
    public void enregistrerPaiementPartiel(BigDecimal montantPaye, String methodePaiement, String referencePaiement) {
        if (cotisationSelectionnee == null) {
            return;
        }
        
        try {
            cotisationSelectionnee.setStatut("PARTIELLEMENT_PAYEE");
            cotisationSelectionnee.setMontantPaye(montantPaye);
            cotisationSelectionnee.setMethodePaiement(methodePaiement);
            cotisationSelectionnee.setReferencePaiement(referencePaiement);
            cotisationSelectionnee.setDatePaiement(LocalDateTime.now());
            
            cotisationService.modifier(cotisationSelectionnee.getId(), cotisationSelectionnee);
            
            // Recharger les données
            chargerCotisations();
            chargerStatistiques();
            appliquerFiltres();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Paiement partiel enregistré"));
            LOGGER.info("Paiement partiel enregistré: " + cotisationSelectionnee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'enregistrement du paiement partiel: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible d'enregistrer le paiement: " + e.getMessage()));
        }
    }
    
    /**
     * Sélectionne une cotisation pour afficher ses détails
     */
    public void selectionnerCotisation(CotisationDTO cotisation) {
        this.cotisationSelectionnee = cotisation;
    }
    
    /**
     * Envoie un rappel pour une cotisation
     */
    public void envoyerRappel() {
        if (cotisationSelectionnee == null || cotisationSelectionnee.getMembreId() == null) {
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_WARN, "Attention", 
                    "Aucune cotisation sélectionnée"));
            return;
        }
        
        try {
            String message = "Rappel: Votre cotisation de " + formatMontant(cotisationSelectionnee.getMontantDu()) 
                + " est en attente de paiement.";
            
            notificationService.envoyerNotificationGroupe(
                "RAPPEL_COTISATION",
                "Rappel de cotisation",
                message,
                List.of(cotisationSelectionnee.getMembreId().toString())
            );
            
            LOGGER.info("Rappel envoyé à: " + cotisationSelectionnee.getNomMembre());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Rappel", 
                    "Rappel envoyé à " + cotisationSelectionnee.getNomMembre()));
        } catch (Exception e) {
            LOGGER.severe("Erreur envoi rappel: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible d'envoyer le rappel: " + e.getMessage()));
        }
    }
    
    /**
     * Envoie des rappels groupés
     */
    public void envoyerRappelsGroupes() {
        if (cotisationsSelectionnees == null || cotisationsSelectionnees.isEmpty()) {
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_WARN, "Attention", 
                    "Aucune cotisation sélectionnée"));
            return;
        }
        
        try {
            List<String> destinataires = cotisationsSelectionnees.stream()
                .filter(c -> c.getMembreId() != null)
                .map(c -> c.getMembreId().toString())
                .distinct()
                .collect(Collectors.toList());
            
            BigDecimal montantTotal = cotisationsSelectionnees.stream()
                .map(c -> c.getMontantDu() != null ? c.getMontantDu() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            notificationService.envoyerNotificationGroupe(
                "RAPPEL_COTISATION",
                "Rappel de paiement",
                "Vous avez des cotisations en attente. Montant total: " + formatMontant(montantTotal),
                destinataires
            );
            
            LOGGER.info("Rappels envoyés à " + destinataires.size() + " membres");
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Rappels", 
                    destinataires.size() + " rappel(s) envoyé(s)"));
        } catch (Exception e) {
            LOGGER.severe("Erreur envoi rappels groupés: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible d'envoyer les rappels: " + e.getMessage()));
        }
    }
    
    /**
     * Exporte les cotisations en CSV
     */
    public void exporterCotisations() {
        try {
            LOGGER.info("Export de " + cotisationsFiltrees.size() + " cotisations");
            
            if (cotisationsFiltrees.isEmpty()) {
                FacesContext.getCurrentInstance().addMessage(null, 
                    new FacesMessage(FacesMessage.SEVERITY_WARN, "Attention", 
                        "Aucune cotisation à exporter"));
                return;
            }
            
            List<UUID> ids = cotisationsFiltrees.stream()
                .map(CotisationDTO::getId)
                .filter(id -> id != null)
                .collect(Collectors.toList());
            
            byte[] csvData = exportService.exporterCotisationsSelectionneesCSV(ids);
            
            telechargerFichier(csvData, "cotisations-export.csv", "text/csv");
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Export", 
                    "Export de " + cotisationsFiltrees.size() + " cotisation(s) terminé"));
        } catch (Exception e) {
            LOGGER.severe("Erreur export cotisations: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible d'exporter les cotisations: " + e.getMessage()));
        }
    }
    
    /**
     * Génère un rapport financier mensuel
     */
    public void genererRapportFinancier() {
        try {
            LOGGER.info("Rapport financier généré");
            
            int annee = LocalDate.now().getYear();
            int mois = LocalDate.now().getMonthValue();
            
            byte[] rapport = exportService.genererRapportMensuel(annee, mois, null);
            
            telechargerFichier(rapport, "rapport-financier-" + annee + "-" + String.format("%02d", mois) + ".txt", "text/plain");
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Rapport", 
                    "Rapport financier généré avec succès"));
        } catch (Exception e) {
            LOGGER.severe("Erreur génération rapport: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de générer le rapport: " + e.getMessage()));
        }
    }
    
    /**
     * Télécharge un fichier via le navigateur
     */
    private void telechargerFichier(byte[] data, String nomFichier, String contentType) {
        try {
            FacesContext fc = FacesContext.getCurrentInstance();
            ExternalContext ec = fc.getExternalContext();
            
            ec.responseReset();
            ec.setResponseContentType(contentType + "; charset=UTF-8");
            ec.setResponseContentLength(data.length);
            ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + nomFichier + "\"");
            
            OutputStream output = ec.getResponseOutputStream();
            output.write(data);
            output.flush();
            
            fc.responseComplete();
        } catch (Exception e) {
            LOGGER.severe("Erreur téléchargement fichier: " + e.getMessage());
            throw new RuntimeException("Erreur lors du téléchargement", e);
        }
    }
    
    /**
     * Formate un montant en FCFA
     */
    private String formatMontant(BigDecimal montant) {
        if (montant == null) return "0 FCFA";
        return String.format("%,.0f FCFA", montant.doubleValue());
    }
    
    /**
     * Actualise les données depuis le backend
     */
    public void actualiser() {
        chargerCotisations();
        chargerStatistiques();
        chargerEvolutionPaiements();
        chargerRepartitionMethodes();
        chargerRappels();
        appliquerFiltres();
        FacesContext.getCurrentInstance().addMessage(null, 
            new FacesMessage(FacesMessage.SEVERITY_INFO, "Actualisation", 
                "Données actualisées"));
    }
    
    /**
     * Compte les cotisations par statut
     */
    public long compterParStatut(String statut) {
        if (cotisationsFiltrees == null) return 0;
        return cotisationsFiltrees.stream()
            .filter(c -> statut.equals(c.getStatut()))
            .count();
    }
    
    /**
     * Compte les cotisations par type
     */
    public long compterParType(String type) {
        if (cotisationsFiltrees == null) return 0;
        return cotisationsFiltrees.stream()
            .filter(c -> type.equals(c.getTypeCotisation()))
            .count();
    }
    
    // Getters et Setters
    
    public List<CotisationDTO> getToutesLesCotisations() { 
        return toutesLesCotisations; 
    }
    
    public void setToutesLesCotisations(List<CotisationDTO> toutesLesCotisations) { 
        this.toutesLesCotisations = toutesLesCotisations; 
    }
    
    public List<CotisationDTO> getCotisationsFiltrees() { 
        return cotisationsFiltrees; 
    }
    
    public void setCotisationsFiltrees(List<CotisationDTO> cotisationsFiltrees) { 
        this.cotisationsFiltrees = cotisationsFiltrees; 
    }
    
    public List<CotisationDTO> getCotisationsSelectionnees() { 
        return cotisationsSelectionnees; 
    }
    
    public void setCotisationsSelectionnees(List<CotisationDTO> cotisationsSelectionnees) { 
        this.cotisationsSelectionnees = cotisationsSelectionnees; 
    }
    
    public CotisationDTO getCotisationSelectionnee() { 
        return cotisationSelectionnee; 
    }
    
    public void setCotisationSelectionnee(CotisationDTO cotisationSelectionnee) { 
        this.cotisationSelectionnee = cotisationSelectionnee; 
    }
    
    public NouvelleCotisation getNouvelleCotisation() { 
        return nouvelleCotisation; 
    }
    
    public void setNouvelleCotisation(NouvelleCotisation nouvelleCotisation) { 
        this.nouvelleCotisation = nouvelleCotisation; 
    }
    
    public Filtres getFiltres() { 
        return filtres; 
    }
    
    public void setFiltres(Filtres filtres) { 
        this.filtres = filtres; 
    }
    
    public StatistiquesFinancieres getStatistiques() { 
        return statistiques; 
    }
    
    public void setStatistiques(StatistiquesFinancieres statistiques) { 
        this.statistiques = statistiques; 
    }
    
    public List<EvolutionPaiement> getEvolutionPaiements() { 
        return evolutionPaiements; 
    }
    
    public void setEvolutionPaiements(List<EvolutionPaiement> evolutionPaiements) { 
        this.evolutionPaiements = evolutionPaiements; 
    }
    
    public List<RepartitionMethode> getRepartitionMethodes() { 
        return repartitionMethodes; 
    }
    
    public void setRepartitionMethodes(List<RepartitionMethode> repartitionMethodes) { 
        this.repartitionMethodes = repartitionMethodes; 
    }
    
    public List<RappelCotisation> getRappelsEnAttente() { 
        return rappelsEnAttente; 
    }
    
    public void setRappelsEnAttente(List<RappelCotisation> rappelsEnAttente) { 
        this.rappelsEnAttente = rappelsEnAttente; 
    }
    
    // Classes internes pour les formulaires et données d'affichage
    
    /**
     * Classe pour le formulaire de nouvelle cotisation
     */
    public static class NouvelleCotisation implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private UUID membreId;
        private String typeCotisation;
        private String libelle;
        private String description;
        private BigDecimal montantDu;
        private LocalDate dateEcheance;
        private String observations;
        
        // Getters et setters
        public UUID getMembreId() { return membreId; }
        public void setMembreId(UUID membreId) { this.membreId = membreId; }
        
        public String getTypeCotisation() { return typeCotisation; }
        public void setTypeCotisation(String typeCotisation) { this.typeCotisation = typeCotisation; }
        
        public String getLibelle() { return libelle; }
        public void setLibelle(String libelle) { this.libelle = libelle; }
        
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
        
        public BigDecimal getMontantDu() { return montantDu; }
        public void setMontantDu(BigDecimal montantDu) { this.montantDu = montantDu; }
        
        public LocalDate getDateEcheance() { return dateEcheance; }
        public void setDateEcheance(LocalDate dateEcheance) { this.dateEcheance = dateEcheance; }
        
        public String getObservations() { return observations; }
        public void setObservations(String observations) { this.observations = observations; }
    }
    
    /**
     * Classe pour les filtres de recherche
     */
    public static class Filtres implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String nomMembre;
        private String club;
        private String statut;
        private String typeCotisation;
        private String methodePaiement;
        private LocalDate dateDebut;
        private LocalDate dateFin;
        
        // Getters et setters
        public String getNomMembre() { return nomMembre; }
        public void setNomMembre(String nomMembre) { this.nomMembre = nomMembre; }
        
        public String getClub() { return club; }
        public void setClub(String club) { this.club = club; }
        
        public String getStatut() { return statut; }
        public void setStatut(String statut) { this.statut = statut; }
        
        public String getTypeCotisation() { return typeCotisation; }
        public void setTypeCotisation(String typeCotisation) { this.typeCotisation = typeCotisation; }
        
        public String getMethodePaiement() { return methodePaiement; }
        public void setMethodePaiement(String methodePaiement) { this.methodePaiement = methodePaiement; }
        
        public LocalDate getDateDebut() { return dateDebut; }
        public void setDateDebut(LocalDate dateDebut) { this.dateDebut = dateDebut; }
        
        public LocalDate getDateFin() { return dateFin; }
        public void setDateFin(LocalDate dateFin) { this.dateFin = dateFin; }
    }
    
    /**
     * Classe pour les statistiques financières
     */
    public static class StatistiquesFinancieres implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private BigDecimal totalCollecte;
        private BigDecimal objectifAnnuel;
        private double tauxRecouvrement;
        private int cotisationsEnRetard;
        private BigDecimal montantRetard;
        private BigDecimal moyenneMensuelle;
        
        // Getters et setters
        public BigDecimal getTotalCollecte() { return totalCollecte; }
        public void setTotalCollecte(BigDecimal totalCollecte) { this.totalCollecte = totalCollecte; }
        
        public BigDecimal getObjectifAnnuel() { return objectifAnnuel; }
        public void setObjectifAnnuel(BigDecimal objectifAnnuel) { this.objectifAnnuel = objectifAnnuel; }
        
        public double getTauxRecouvrement() { return tauxRecouvrement; }
        public void setTauxRecouvrement(double tauxRecouvrement) { this.tauxRecouvrement = tauxRecouvrement; }
        
        public int getCotisationsEnRetard() { return cotisationsEnRetard; }
        public void setCotisationsEnRetard(int cotisationsEnRetard) { this.cotisationsEnRetard = cotisationsEnRetard; }
        
        public BigDecimal getMontantRetard() { return montantRetard; }
        public void setMontantRetard(BigDecimal montantRetard) { this.montantRetard = montantRetard; }
        
        public BigDecimal getMoyenneMensuelle() { return moyenneMensuelle; }
        public void setMoyenneMensuelle(BigDecimal moyenneMensuelle) { this.moyenneMensuelle = moyenneMensuelle; }
        
        // Méthodes de formatage
        public String getTotalCollecteFormatte() {
            if (totalCollecte == null) return "0 FCFA";
            return String.format("%,.0f FCFA", totalCollecte.doubleValue());
        }
        
        public String getObjectifAnnuelFormatte() {
            if (objectifAnnuel == null) return "0 FCFA";
            return String.format("%,.0f FCFA", objectifAnnuel.doubleValue());
        }
        
        public String getMontantRetardFormatte() {
            if (montantRetard == null) return "0 FCFA";
            return String.format("%,.0f FCFA", montantRetard.doubleValue());
        }
        
        public String getMoyenneMensuelleFormattee() {
            if (moyenneMensuelle == null) return "0 FCFA";
            return String.format("%,.0f FCFA", moyenneMensuelle.doubleValue());
        }
        
        public int getTauxRecouvrementInt() {
            return (int) tauxRecouvrement;
        }
    }
    
    /**
     * Classe pour l'évolution des paiements (graphique)
     */
    public static class EvolutionPaiement implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String mois;
        private BigDecimal montant;
        private int hauteur;
        
        // Getters et setters
        public String getMois() { return mois; }
        public void setMois(String mois) { this.mois = mois; }
        
        public BigDecimal getMontant() { return montant; }
        public void setMontant(BigDecimal montant) { this.montant = montant; }
        
        public int getHauteur() { return hauteur; }
        public void setHauteur(int hauteur) { this.hauteur = hauteur; }
        
        public String getMontantFormatte() {
            if (montant == null) return "0";
            return String.format("%.1fM", montant.divide(new BigDecimal("1000000"), 1, java.math.RoundingMode.HALF_UP).doubleValue());
        }
    }
    
    /**
     * Classe pour la répartition par méthode de paiement
     */
    public static class RepartitionMethode implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String methode;
        private double pourcentage;
        private BigDecimal montant;
        private String couleur;
        private String icon;
        
        // Getters et setters
        public String getMethode() { return methode; }
        public void setMethode(String methode) { this.methode = methode; }
        
        public double getPourcentage() { return pourcentage; }
        public void setPourcentage(double pourcentage) { this.pourcentage = pourcentage; }
        
        public BigDecimal getMontant() { return montant; }
        public void setMontant(BigDecimal montant) { this.montant = montant; }
        
        public String getCouleur() { return couleur; }
        public void setCouleur(String couleur) { this.couleur = couleur; }
        
        public String getIcon() { return icon; }
        public void setIcon(String icon) { this.icon = icon; }
        
        public String getMontantFormatte() {
            if (montant == null) return "0 FCFA";
            return String.format("%,.0f FCFA", montant.doubleValue());
        }
        
        public int getPourcentageInt() {
            return (int) pourcentage;
        }
        
        public int getLargeur() {
            return (int) (pourcentage * 2);
        }
    }
    
    /**
     * Classe pour les rappels de cotisation
     */
    public static class RappelCotisation implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String nomMembre;
        private String club;
        private BigDecimal montantDu;
        private int joursRetard;
        private String priorite;
        
        // Getters et setters
        public String getNomMembre() { return nomMembre; }
        public void setNomMembre(String nomMembre) { this.nomMembre = nomMembre; }
        
        public String getClub() { return club; }
        public void setClub(String club) { this.club = club; }
        
        public BigDecimal getMontantDu() { return montantDu; }
        public void setMontantDu(BigDecimal montantDu) { this.montantDu = montantDu; }
        
        public int getJoursRetard() { return joursRetard; }
        public void setJoursRetard(int joursRetard) { this.joursRetard = joursRetard; }
        
        public String getPriorite() { return priorite; }
        public void setPriorite(String priorite) { this.priorite = priorite; }
        
        public String getMontantDuFormatte() {
            if (montantDu == null) return "0 FCFA";
            return String.format("%,.0f FCFA", montantDu.doubleValue());
        }
        
        public String getPrioriteSeverity() {
            if (priorite == null) return "secondary";
            return switch (priorite) {
                case "HAUTE" -> "danger";
                case "MOYENNE" -> "warning";
                case "FAIBLE" -> "info";
                default -> "secondary";
            };
        }
    }
}
