package dev.lions.unionflow.client.view;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
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.AdhesionDTO;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.dto.MembreDTO;
import dev.lions.unionflow.client.service.AdhesionService;
import dev.lions.unionflow.client.service.AssociationService;
import dev.lions.unionflow.client.service.MembreService;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.model.SelectItem;
import jakarta.inject.Inject;
import jakarta.inject.Named;

/**
 * Bean JSF pour la gestion des adhésions
 * Utilise directement AdhesionDTO et se connecte au backend
 * 
 * @author UnionFlow Team
 * @version 1.0
 */
@Named("adhesionsBean")
@SessionScoped
public class AdhesionsBean implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = Logger.getLogger(AdhesionsBean.class.getName());
    
    @Inject
    @RestClient
    private AdhesionService adhesionService;
    
    @Inject
    @RestClient
    private MembreService membreService;
    
    @Inject
    @RestClient
    private AssociationService associationService;
    
    // Listes de référence pour les select
    private List<MembreDTO> listeMembres;
    private List<AssociationDTO> listeAssociations;
    
    // Données principales
    private List<AdhesionDTO> toutesLesAdhesions;
    private List<AdhesionDTO> adhesionsFiltrees;
    private List<AdhesionDTO> adhesionsSelectionnees;
    private AdhesionDTO adhesionSelectionnee;
    
    // Formulaire nouvelle adhésion
    private NouvelleAdhesion nouvelleAdhesion;
    
    // Filtres
    private FiltresAdhesion filtres;
    
    // Statistiques
    private StatistiquesAdhesion statistiques;
    
    @PostConstruct
    public void init() {
        initializeFiltres();
        chargerMembresEtAssociations();
        chargerAdhesions();
        chargerStatistiques();
        initializeNouvelleAdhesion();
        appliquerFiltres();
    }
    
    /**
     * Charge les listes de membres et d'associations depuis le backend
     */
    private void chargerMembresEtAssociations() {
        listeMembres = new ArrayList<>();
        listeAssociations = new ArrayList<>();
        
        try {
            listeMembres = membreService.listerActifs();
            LOGGER.info("Chargement de " + listeMembres.size() + " membres actifs");
        } catch (Exception e) {
            LOGGER.warning("Impossible de charger les membres: " + e.getMessage());
        }
        
        try {
            listeAssociations = associationService.listerToutes(0, 1000);
            LOGGER.info("Chargement de " + listeAssociations.size() + " associations actives");
        } catch (Exception e) {
            LOGGER.warning("Impossible de charger les associations: " + e.getMessage());
        }
    }
    
    /**
     * Retourne la liste des membres pour les SelectItem
     */
    public List<SelectItem> getMembresSelectItems() {
        List<SelectItem> items = new ArrayList<>();
        items.add(new SelectItem(null, "Sélectionner un membre"));
        if (listeMembres != null) {
            for (MembreDTO membre : listeMembres) {
                String label = membre.getPrenom() + " " + membre.getNom();
                if (membre.getNumeroMembre() != null) {
                    label += " (" + membre.getNumeroMembre() + ")";
                }
                items.add(new SelectItem(membre.getId(), label));
            }
        }
        return items;
    }
    
    /**
     * Retourne la liste des associations pour les SelectItem
     */
    public List<SelectItem> getAssociationsSelectItems() {
        List<SelectItem> items = new ArrayList<>();
        items.add(new SelectItem(null, "Sélectionner une organisation"));
        if (listeAssociations != null) {
            for (AssociationDTO assoc : listeAssociations) {
                String label = assoc.getNom();
                if (assoc.getTypeAssociation() != null) {
                    label += " (" + assoc.getTypeAssociation() + ")";
                }
                items.add(new SelectItem(assoc.getId(), label));
            }
        }
        return items;
    }
    
    private void initializeFiltres() {
        filtres = new FiltresAdhesion();
        adhesionsSelectionnees = new ArrayList<>();
    }
    
    /**
     * Charge les adhésions depuis le backend
     */
    private void chargerAdhesions() {
        toutesLesAdhesions = new ArrayList<>();
        try {
            toutesLesAdhesions = adhesionService.listerToutes(0, 1000);
            LOGGER.info("Chargement de " + toutesLesAdhesions.size() + " adhésions");
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des adhésions: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de charger les adhésions: " + e.getMessage()));
        }
    }
    
    /**
     * Charge les statistiques depuis le backend
     */
    private void chargerStatistiques() {
        statistiques = new StatistiquesAdhesion();
        try {
            Map<String, Object> statsBackend = adhesionService.obtenirStatistiques();
            
            // Extraction des statistiques du backend
            Long totalAdhesions = ((Number) statsBackend.getOrDefault("totalAdhesions", 0L)).longValue();
            Long adhesionsApprouvees = ((Number) statsBackend.getOrDefault("adhesionsApprouvees", 0L)).longValue();
            Long adhesionsEnAttente = ((Number) statsBackend.getOrDefault("adhesionsEnAttente", 0L)).longValue();
            Long adhesionsPayees = ((Number) statsBackend.getOrDefault("adhesionsPayees", 0L)).longValue();
            Double tauxApprobation = ((Number) statsBackend.getOrDefault("tauxApprobation", 0.0)).doubleValue();
            Double tauxPaiement = ((Number) statsBackend.getOrDefault("tauxPaiement", 0.0)).doubleValue();
            
            // Calcul des montants depuis les adhésions réelles
            BigDecimal totalCollecte = toutesLesAdhesions.stream()
                .filter(a -> "PAYEE".equals(a.getStatut()) || "EN_PAIEMENT".equals(a.getStatut()))
                .map(a -> a.getMontantPaye() != null ? a.getMontantPaye() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            BigDecimal totalFrais = toutesLesAdhesions.stream()
                .map(a -> a.getFraisAdhesion() != null ? a.getFraisAdhesion() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            statistiques.setTotalAdhesions(totalAdhesions.intValue());
            statistiques.setAdhesionsApprouvees(adhesionsApprouvees.intValue());
            statistiques.setAdhesionsEnAttente(adhesionsEnAttente.intValue());
            statistiques.setAdhesionsPayees(adhesionsPayees.intValue());
            statistiques.setTauxApprobation(tauxApprobation);
            statistiques.setTauxPaiement(tauxPaiement);
            statistiques.setTotalCollecte(totalCollecte);
            statistiques.setTotalFrais(totalFrais);
            
            LOGGER.info("Statistiques chargées: Total=" + totalAdhesions + ", Approuvées=" + adhesionsApprouvees);
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des statistiques: " + e.getMessage());
            initialiserStatistiquesParDefaut();
        }
    }
    
    private void initialiserStatistiquesParDefaut() {
        statistiques.setTotalAdhesions(0);
        statistiques.setAdhesionsApprouvees(0);
        statistiques.setAdhesionsEnAttente(0);
        statistiques.setAdhesionsPayees(0);
        statistiques.setTauxApprobation(0.0);
        statistiques.setTauxPaiement(0.0);
        statistiques.setTotalCollecte(BigDecimal.ZERO);
        statistiques.setTotalFrais(BigDecimal.ZERO);
    }
    
    /**
     * Applique les filtres en utilisant la recherche backend
     */
    private void appliquerFiltres() {
        try {
            // Utiliser la recherche backend
            if (filtres.getStatut() != null && !filtres.getStatut().isEmpty()) {
                adhesionsFiltrees = adhesionService.obtenirParStatut(filtres.getStatut(), 0, 1000);
            } else {
                adhesionsFiltrees = new ArrayList<>(toutesLesAdhesions);
            }
            
            // Appliquer les filtres supplémentaires côté client si nécessaire
            if (filtres.getNomMembre() != null && !filtres.getNomMembre().trim().isEmpty()) {
                adhesionsFiltrees = adhesionsFiltrees.stream()
                    .filter(a -> a.getNomMembre() != null 
                        && a.getNomMembre().toLowerCase().contains(filtres.getNomMembre().toLowerCase()))
                    .collect(Collectors.toList());
            }
            
            if (filtres.getDateDebut() != null) {
                adhesionsFiltrees = adhesionsFiltrees.stream()
                    .filter(a -> a.getDateDemande() != null 
                        && !a.getDateDemande().isBefore(filtres.getDateDebut()))
                    .collect(Collectors.toList());
            }
            
            if (filtres.getDateFin() != null) {
                adhesionsFiltrees = adhesionsFiltrees.stream()
                    .filter(a -> a.getDateDemande() != null 
                        && !a.getDateDemande().isAfter(filtres.getDateFin()))
                    .collect(Collectors.toList());
            }
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'application des filtres: " + e.getMessage());
            adhesionsFiltrees = new ArrayList<>();
        }
    }
    
    // Actions
    
    /**
     * Recherche avec filtres
     */
    public void rechercher() {
        appliquerFiltres();
        FacesContext.getCurrentInstance().addMessage(null, 
            new FacesMessage(FacesMessage.SEVERITY_INFO, "Recherche", 
                adhesionsFiltrees.size() + " adhésion(s) trouvée(s)"));
    }
    
    /**
     * Réinitialise les filtres
     */
    public void reinitialiserFiltres() {
        filtres = new FiltresAdhesion();
        chargerAdhesions();
        appliquerFiltres();
    }
    
    /**
     * Enregistre une nouvelle adhésion via le backend
     */
    public void enregistrerAdhesion() {
        try {
            AdhesionDTO nouvelleAdh = new AdhesionDTO();
            nouvelleAdh.setMembreId(nouvelleAdhesion.getMembreId());
            nouvelleAdh.setOrganisationId(nouvelleAdhesion.getOrganisationId());
            nouvelleAdh.setFraisAdhesion(nouvelleAdhesion.getFraisAdhesion());
            nouvelleAdh.setDateDemande(LocalDate.now());
            nouvelleAdh.setStatut("EN_ATTENTE");
            nouvelleAdh.setMontantPaye(BigDecimal.ZERO);
            nouvelleAdh.setCodeDevise("XOF");
            nouvelleAdh.setObservations(nouvelleAdhesion.getObservations());
            
            AdhesionDTO adhesionCreee = adhesionService.creer(nouvelleAdh);
            
            // Recharger les données
            chargerAdhesions();
            chargerStatistiques();
            appliquerFiltres();
            initializeNouvelleAdhesion();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Adhésion créée avec succès"));
            LOGGER.info("Nouvelle adhésion créée: " + adhesionCreee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la création de l'adhésion: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de créer l'adhésion: " + e.getMessage()));
        }
    }
    
    /**
     * Approuve une adhésion
     */
    public void approuverAdhesion() {
        if (adhesionSelectionnee == null) {
            return;
        }
        
        try {
            adhesionService.approuver(adhesionSelectionnee.getId(), "Admin");
            
            // Recharger les données
            chargerAdhesions();
            chargerStatistiques();
            appliquerFiltres();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Adhésion approuvée"));
            LOGGER.info("Adhésion approuvée: " + adhesionSelectionnee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'approbation: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible d'approuver l'adhésion: " + e.getMessage()));
        }
    }
    
    /**
     * Rejette une adhésion
     */
    public void rejeterAdhesion(String motifRejet) {
        if (adhesionSelectionnee == null) {
            return;
        }
        
        try {
            adhesionService.rejeter(adhesionSelectionnee.getId(), motifRejet);
            
            // Recharger les données
            chargerAdhesions();
            chargerStatistiques();
            appliquerFiltres();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Adhésion rejetée"));
            LOGGER.info("Adhésion rejetée: " + adhesionSelectionnee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du rejet: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de rejeter l'adhésion: " + e.getMessage()));
        }
    }
    
    /**
     * Enregistre un paiement pour une adhésion
     */
    public void enregistrerPaiement(BigDecimal montantPaye, String methodePaiement, String referencePaiement) {
        if (adhesionSelectionnee == null) {
            return;
        }
        
        try {
            adhesionService.enregistrerPaiement(
                adhesionSelectionnee.getId(), 
                montantPaye, 
                methodePaiement, 
                referencePaiement);
            
            // Recharger les données
            chargerAdhesions();
            chargerStatistiques();
            appliquerFiltres();
            
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", 
                    "Paiement enregistré"));
            LOGGER.info("Paiement enregistré pour l'adhésion: " + adhesionSelectionnee.getId());
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'enregistrement du paiement: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible d'enregistrer le paiement: " + e.getMessage()));
        }
    }
    
    /**
     * Sélectionne une adhésion pour afficher ses détails
     */
    public void selectionnerAdhesion(AdhesionDTO adhesion) {
        this.adhesionSelectionnee = adhesion;
    }
    
    /**
     * Actualise les données depuis le backend
     */
    public void actualiser() {
        chargerAdhesions();
        chargerStatistiques();
        appliquerFiltres();
        FacesContext.getCurrentInstance().addMessage(null, 
            new FacesMessage(FacesMessage.SEVERITY_INFO, "Actualisation", 
                "Données actualisées"));
    }
    
    /**
     * Charge les adhésions en attente depuis le backend
     */
    public void chargerAdhesionsEnAttente() {
        try {
            toutesLesAdhesions = adhesionService.obtenirEnAttente(0, 1000);
            appliquerFiltres();
            LOGGER.info("Chargement de " + toutesLesAdhesions.size() + " adhésions en attente");
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des adhésions en attente: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", 
                    "Impossible de charger les adhésions en attente: " + e.getMessage()));
        }
    }
    
    private void initializeNouvelleAdhesion() {
        nouvelleAdhesion = new NouvelleAdhesion();
    }
    
    // Getters et Setters
    
    public List<AdhesionDTO> getToutesLesAdhesions() { 
        return toutesLesAdhesions; 
    }
    
    public void setToutesLesAdhesions(List<AdhesionDTO> toutesLesAdhesions) { 
        this.toutesLesAdhesions = toutesLesAdhesions; 
    }
    
    public List<AdhesionDTO> getAdhesionsFiltrees() { 
        return adhesionsFiltrees; 
    }
    
    public void setAdhesionsFiltrees(List<AdhesionDTO> adhesionsFiltrees) { 
        this.adhesionsFiltrees = adhesionsFiltrees; 
    }
    
    public List<AdhesionDTO> getAdhesionsSelectionnees() { 
        return adhesionsSelectionnees; 
    }
    
    public void setAdhesionsSelectionnees(List<AdhesionDTO> adhesionsSelectionnees) { 
        this.adhesionsSelectionnees = adhesionsSelectionnees; 
    }
    
    public AdhesionDTO getAdhesionSelectionnee() { 
        return adhesionSelectionnee; 
    }
    
    public void setAdhesionSelectionnee(AdhesionDTO adhesionSelectionnee) { 
        this.adhesionSelectionnee = adhesionSelectionnee; 
    }
    
    public NouvelleAdhesion getNouvelleAdhesion() { 
        return nouvelleAdhesion; 
    }
    
    public void setNouvelleAdhesion(NouvelleAdhesion nouvelleAdhesion) { 
        this.nouvelleAdhesion = nouvelleAdhesion; 
    }
    
    public FiltresAdhesion getFiltres() { 
        return filtres; 
    }
    
    public void setFiltres(FiltresAdhesion filtres) { 
        this.filtres = filtres; 
    }
    
    public StatistiquesAdhesion getStatistiques() { 
        return statistiques; 
    }
    
    public void setStatistiques(StatistiquesAdhesion statistiques) { 
        this.statistiques = statistiques; 
    }
    
    // Classes internes pour les formulaires et filtres
    
    public static class NouvelleAdhesion implements Serializable {
        private static final long serialVersionUID = 1L;
        private UUID membreId;
        private UUID organisationId;
        private BigDecimal fraisAdhesion;
        private String observations;
        
        // Getters et Setters
        public UUID getMembreId() { return membreId; }
        public void setMembreId(UUID membreId) { this.membreId = membreId; }
        
        public UUID getOrganisationId() { return organisationId; }
        public void setOrganisationId(UUID organisationId) { this.organisationId = organisationId; }
        
        public BigDecimal getFraisAdhesion() { return fraisAdhesion; }
        public void setFraisAdhesion(BigDecimal fraisAdhesion) { this.fraisAdhesion = fraisAdhesion; }
        
        public String getObservations() { return observations; }
        public void setObservations(String observations) { this.observations = observations; }
    }
    
    public static class FiltresAdhesion implements Serializable {
        private static final long serialVersionUID = 1L;
        private String statut;
        private String nomMembre;
        private LocalDate dateDebut;
        private LocalDate dateFin;
        
        // Getters et Setters
        public String getStatut() { return statut; }
        public void setStatut(String statut) { this.statut = statut; }
        
        public String getNomMembre() { return nomMembre; }
        public void setNomMembre(String nomMembre) { this.nomMembre = nomMembre; }
        
        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; }
    }
    
    public static class StatistiquesAdhesion implements Serializable {
        private static final long serialVersionUID = 1L;
        private int totalAdhesions;
        private int adhesionsApprouvees;
        private int adhesionsEnAttente;
        private int adhesionsPayees;
        private double tauxApprobation;
        private double tauxPaiement;
        private BigDecimal totalCollecte;
        private BigDecimal totalFrais;
        
        // Getters et Setters
        public int getTotalAdhesions() { return totalAdhesions; }
        public void setTotalAdhesions(int totalAdhesions) { this.totalAdhesions = totalAdhesions; }
        
        public int getAdhesionsApprouvees() { return adhesionsApprouvees; }
        public void setAdhesionsApprouvees(int adhesionsApprouvees) { this.adhesionsApprouvees = adhesionsApprouvees; }
        
        public int getAdhesionsEnAttente() { return adhesionsEnAttente; }
        public void setAdhesionsEnAttente(int adhesionsEnAttente) { this.adhesionsEnAttente = adhesionsEnAttente; }
        
        public int getAdhesionsPayees() { return adhesionsPayees; }
        public void setAdhesionsPayees(int adhesionsPayees) { this.adhesionsPayees = adhesionsPayees; }
        
        public double getTauxApprobation() { return tauxApprobation; }
        public void setTauxApprobation(double tauxApprobation) { this.tauxApprobation = tauxApprobation; }
        
        public double getTauxPaiement() { return tauxPaiement; }
        public void setTauxPaiement(double tauxPaiement) { this.tauxPaiement = tauxPaiement; }
        
        public BigDecimal getTotalCollecte() { return totalCollecte; }
        public void setTotalCollecte(BigDecimal totalCollecte) { this.totalCollecte = totalCollecte; }
        
        public BigDecimal getTotalFrais() { return totalFrais; }
        public void setTotalFrais(BigDecimal totalFrais) { this.totalFrais = totalFrais; }
        
        // Méthodes utilitaires pour l'affichage
        public String getTotalCollecteFormatte() {
            if (totalCollecte == null) return "0 FCFA";
            return String.format("%,.0f FCFA", totalCollecte.doubleValue());
        }
        
        public String getTotalFraisFormatte() {
            if (totalFrais == null) return "0 FCFA";
            return String.format("%,.0f FCFA", totalFrais.doubleValue());
        }
        
        public int getTauxApprobationInt() {
            return (int) Math.round(tauxApprobation);
        }
        
        public int getTauxPaiementInt() {
            return (int) Math.round(tauxPaiement);
        }
    }
}

