package dev.lions.unionflow.client.view;

import dev.lions.unionflow.server.api.dto.cotisation.response.CotisationResponse;
import dev.lions.unionflow.server.api.dto.membre.response.MembreResponse;
import dev.lions.unionflow.server.api.dto.paiement.response.PaiementResponse;
import dev.lions.unionflow.client.service.CotisationService;
import dev.lions.unionflow.client.service.ErrorHandlerService;
import dev.lions.unionflow.client.service.ExportClientService;
import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.client.service.RetryService;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.annotation.PostConstruct;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;

import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Bean pour le paiement des cotisations du membre connecté (MEMBRE_ACTIF).
 * Affiche uniquement les cotisations personnelles, pas les données admin.
 *
 * Pattern DRY: Réutilise la logique de MembreCotisationBean et DashboardMembreBean
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-02
 */
@Named("mesCotisationsPaiementBean")
@ViewScoped
public class MesCotisationsPaiementBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(MesCotisationsPaiementBean.class);
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy");

    @Inject
    SecurityIdentity securityIdentity;

    @Inject
    @RestClient
    private MembreService membreService;

    @Inject
    @RestClient
    private CotisationService cotisationService;

    @Inject
    @RestClient
    private ExportClientService exportService;

    @Inject
    ErrorHandlerService errorHandler;

    @Inject
    RetryService retryService;

    // Informations du membre connecté
    private UUID membreId;
    private String numeroMembre;
    private MembreResponse membre;

    // KPI personnels - Cotisations à payer
    private Integer cotisationsEnAttente = 0;
    private String montantDu = "0 FCFA";
    private String prochaineEcheance = "-";
    private String totalPaye = "0 FCFA";
    private Integer anneeEnCours = LocalDate.now().getYear();

    // Listes
    private List<CotisationPerso> mesCotisationsEnAttente = new ArrayList<>();
    private List<PaiementPerso> derniersPaiements = new ArrayList<>();

    // Formulaires dialogs
    // Dialog Paiement en Ligne
    private UUID cotisationSelectionneeId;
    private CotisationPerso cotisationSelectionnee;
    private String methodePaiementChoisie = "WAVE";
    private String numeroTelephone;

    // Dialog Paiement Manuel
    private String methodePaiementManuel = "ESPECES";
    private String referencePaiementManuel;
    private String commentairePaiement;

    // Configuration organisation (TODO: charger depuis API)
    private boolean paiementManuelActive = true;

    @PostConstruct
    public void init() {
        LOG.info("Initialisation du bean de paiement des cotisations personnelles");
        detecterMembreConnecte();
        if (membreId != null) {
            chargerDonnees();
        }
    }

    /**
     * Auto-détection du membre connecté (Pattern DRY depuis DashboardMembreBean)
     */
    private void detecterMembreConnecte() {
        try {
            String username = securityIdentity.getPrincipal().getName();
            LOG.infof("Auto-détection du membre connecté: %s", username);

            // Récupérer directement le membre connecté via l'endpoint /me
            membre = retryService.executeWithRetrySupplier(
                () -> membreService.obtenirMembreConnecte(),
                "récupération du membre connecté"
            );

            if (membre != null) {
                membreId = membre.getId();
                numeroMembre = membre.getNumeroMembre();
                LOG.infof("Membre connecté détecté: %s (%s)", numeroMembre, membreId);
            } else {
                LOG.warnf("Aucun membre trouvé pour l'utilisateur: %s", username);
                errorHandler.showWarning("Attention",
                    "Impossible de charger vos cotisations. Veuillez contacter l'administrateur.");
            }
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de l'auto-détection du membre connecté");
            errorHandler.handleException(e, "lors du chargement de vos cotisations", null);
        }
    }

    /**
     * Charge toutes les données personnelles de cotisations et paiements
     */
    private void chargerDonnees() {
        chargerCotisationsEnAttente();
        chargerDerniersPaiements();
        calculerKPI();
    }

    /**
     * Charge les cotisations en attente du membre connecté
     * Pattern DRY: Réutilise la logique de MembreCotisationBean.chargerCotisations()
     */
    private void chargerCotisationsEnAttente() {
        try {
            // TODO: Créer endpoint GET /api/cotisations/mes-cotisations/en-attente
            // Pour l'instant, utiliser l'endpoint existant avec filtre statut
            List<CotisationResponse> cotisationsDTO = retryService.executeWithRetrySupplier(
                () -> cotisationService.rechercher(
                    membreId,
                    "EN_ATTENTE", // Statut
                    null, // Type
                    anneeEnCours,
                    null, // Mois
                    0,
                    100
                ),
                "chargement des cotisations en attente"
            );

            mesCotisationsEnAttente = new ArrayList<>();
            for (CotisationResponse dto : cotisationsDTO) {
                CotisationPerso cotisation = new CotisationPerso();
                cotisation.setId(dto.getId());
                cotisation.setReference(dto.getNumeroReference() != null ? dto.getNumeroReference() : "");
                cotisation.setType(dto.getTypeCotisation() != null ? dto.getTypeCotisation() : "MENSUELLE");
                cotisation.setPeriode(formaterPeriode(dto.getDateEcheance()));
                cotisation.setMontantDu(dto.getMontantDu() != null ? dto.getMontantDu() : BigDecimal.ZERO);
                cotisation.setDateEcheance(dto.getDateEcheance());
                mesCotisationsEnAttente.add(cotisation);
            }

            LOG.infof("Cotisations en attente chargées: %d", mesCotisationsEnAttente.size());
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des cotisations en attente");
            errorHandler.handleException(e, "lors du chargement de vos cotisations en attente", null);
            mesCotisationsEnAttente = new ArrayList<>();
        }
    }

    /**
     * Charge les 5 derniers paiements du membre connecté
     */
    private void chargerDerniersPaiements() {
        try {
            // TODO: Créer endpoint GET /api/paiements/mes-paiements/historique?limit=5
            // Pour l'instant, charger toutes les cotisations payées et prendre les 5 dernières
            List<CotisationResponse> cotisationsPayees = retryService.executeWithRetrySupplier(
                () -> cotisationService.rechercher(
                    membreId,
                    "PAYEE", // Statut
                    null, // Type
                    null, // Année
                    null, // Mois
                    0,
                    5
                ),
                "chargement de l'historique des paiements"
            );

            derniersPaiements = new ArrayList<>();
            for (CotisationResponse dto : cotisationsPayees) {
                PaiementPerso paiement = new PaiementPerso();
                paiement.setId(dto.getId());
                paiement.setReference(dto.getNumeroReference() != null ? dto.getNumeroReference() : "");
                paiement.setPeriode(formaterPeriode(dto.getDateEcheance()));
                paiement.setMontant(dto.getMontantDu() != null ? dto.getMontantDu() : BigDecimal.ZERO);
                if (dto.getDatePaiement() != null) {
                    paiement.setDatePaiement(dto.getDatePaiement().toLocalDate());
                }
                // Note: methodePaiement non disponible dans CotisationResponse
                paiement.setMethodePaiement("Non renseignée");
                derniersPaiements.add(paiement);
            }

            LOG.infof("Derniers paiements chargés: %d", derniersPaiements.size());
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des derniers paiements");
            errorHandler.handleException(e, "lors du chargement de votre historique", null);
            derniersPaiements = new ArrayList<>();
        }
    }

    /**
     * Calcule les KPI personnels depuis les données chargées
     */
    private void calculerKPI() {
        // Cotisations en attente
        cotisationsEnAttente = mesCotisationsEnAttente.size();

        // Montant dû total
        BigDecimal montantTotal = mesCotisationsEnAttente.stream()
            .map(CotisationPerso::getMontantDu)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        montantDu = formaterMontant(montantTotal);

        // Prochaine échéance
        if (!mesCotisationsEnAttente.isEmpty()) {
            LocalDate prochaine = mesCotisationsEnAttente.stream()
                .map(CotisationPerso::getDateEcheance)
                .filter(d -> d != null)
                .min(LocalDate::compareTo)
                .orElse(null);
            prochaineEcheance = prochaine != null ? prochaine.format(DATE_FORMATTER) : "-";
        } else {
            prochaineEcheance = "Aucune";
        }

        // Total payé cette année
        BigDecimal totalPayeAnnee = derniersPaiements.stream()
            .filter(p -> p.getDatePaiement() != null && p.getDatePaiement().getYear() == anneeEnCours)
            .map(PaiementPerso::getMontant)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        totalPaye = formaterMontant(totalPayeAnnee);

        LOG.infof("KPI calculés: %d cotisations en attente, %s à payer", cotisationsEnAttente, montantDu);
    }

    // ═══════════════════════════════════════════════════════════════════════
    // Actions
    // ═══════════════════════════════════════════════════════════════════════

    /**
     * Sélectionne une cotisation et prépare le dialog de paiement en ligne
     */
    public void initierPaiementEnLigne(CotisationPerso cotis) {
        this.cotisationSelectionnee = cotis;
        this.cotisationSelectionneeId = cotis != null ? cotis.getId() : null;
        this.numeroTelephone = null;
        this.methodePaiementChoisie = "WAVE_MONEY";
        LOG.infof("Dialog paiement en ligne ouvert pour cotisation: %s", cotisationSelectionneeId);
    }

    /**
     * Sélectionne une cotisation et prépare le dialog de paiement manuel
     */
    public void selectionnerCotisation(CotisationPerso cotis) {
        this.cotisationSelectionnee = cotis;
        this.cotisationSelectionneeId = cotis != null ? cotis.getId() : null;
        this.methodePaiementManuel = "ESPECES";
        this.referencePaiementManuel = null;
        this.commentairePaiement = null;
        LOG.infof("Dialog paiement manuel ouvert pour cotisation: %s", cotisationSelectionneeId);
    }

    /**
     * Procède au paiement en ligne (Wave, Orange, Free Money, Carte)
     */
    public void procederPaiementEnLigne() {
        if (cotisationSelectionneeId == null) {
            errorHandler.showWarning("Attention", "Veuillez sélectionner une cotisation à payer");
            return;
        }

        if (numeroTelephone == null || numeroTelephone.trim().isEmpty()) {
            errorHandler.showWarning("Attention", "Veuillez saisir votre numéro de téléphone");
            return;
        }

        try {
            // TODO: Créer endpoint POST /api/paiements/initier-paiement-en-ligne
            // Body: { cotisationId, methodePaiement, numeroTelephone }
            // Retour: { redirectUrl, transactionId }
            LOG.infof("Paiement en ligne initié: cotisation=%s, méthode=%s, téléphone=%s",
                cotisationSelectionneeId, methodePaiementChoisie, numeroTelephone);

            errorHandler.showInfo("Paiement en ligne",
                "Redirection vers le gateway de paiement " + methodePaiementChoisie + "...");

            // TODO: Rediriger vers l'URL du gateway de paiement
            // ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
            // ec.redirect(paymentGatewayUrl);

        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de l'initiation du paiement en ligne");
            errorHandler.handleException(e, "lors de l'initiation du paiement", null);
        }
    }

    /**
     * Déclare un paiement manuel (espèces, virement, chèque)
     * Statut: EN_ATTENTE_VALIDATION (le trésorier doit valider)
     */
    public void declarerPaiementManuel() {
        if (cotisationSelectionneeId == null) {
            errorHandler.showWarning("Attention", "Veuillez sélectionner une cotisation");
            return;
        }

        if (methodePaiementManuel == null || methodePaiementManuel.trim().isEmpty()) {
            errorHandler.showWarning("Attention", "Veuillez sélectionner une méthode de paiement");
            return;
        }

        try {
            // TODO: Créer endpoint POST /api/paiements/declarer-paiement-manuel
            // Body: { cotisationId, methodePaiement, reference, commentaire }
            // Retour: 201 Created
            LOG.infof("Paiement manuel déclaré: cotisation=%s, méthode=%s, ref=%s",
                cotisationSelectionneeId, methodePaiementManuel, referencePaiementManuel);

            errorHandler.showSuccess("Paiement déclaré",
                "Votre paiement a été enregistré. Il sera validé par le trésorier.");

            // Recharger les données
            chargerDonnees();

            // Réinitialiser le formulaire
            cotisationSelectionneeId = null;
            methodePaiementManuel = "ESPECES";
            referencePaiementManuel = null;
            commentairePaiement = null;

        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de la déclaration du paiement manuel");
            errorHandler.handleException(e, "lors de la déclaration du paiement", null);
        }
    }

    /**
     * Télécharge le reçu PDF d'un paiement
     */
    public void telechargerRecu(PaiementPerso paiement) {
        if (paiement == null || paiement.getId() == null) {
            errorHandler.showWarning("Attention", "Impossible de télécharger le reçu");
            return;
        }

        UUID paiementId = paiement.getId();
        try {
            // TODO: Créer endpoint GET /api/paiements/telecharger-recu/{id}
            byte[] recu = retryService.executeWithRetrySupplier(
                () -> exportService.genererRecu(paiementId),
                "génération d'un reçu"
            );

            String nomFichier = "recu-" + paiementId + ".pdf";
            telechargerFichier(recu, nomFichier, "application/pdf");

            errorHandler.showSuccess("Reçu téléchargé", "Le reçu a été téléchargé avec succès");
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du téléchargement du reçu");
            errorHandler.handleException(e, "lors du téléchargement du reçu", null);
        }
    }

    /**
     * Actualise les données
     */
    public void actualiser() {
        LOG.info("Actualisation des données de paiement");
        chargerDonnees();
        errorHandler.showSuccess("Actualisation", "Les données ont été actualisées");
    }

    // ═══════════════════════════════════════════════════════════════════════
    // Helpers
    // ═══════════════════════════════════════════════════════════════════════

    private String formaterPeriode(LocalDate dateEcheance) {
        if (dateEcheance == null) {
            return "";
        }
        String[] moisNoms = {"Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
                             "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"};
        int mois = dateEcheance.getMonthValue();
        int annee = dateEcheance.getYear();
        return moisNoms[mois - 1] + " " + annee;
    }

    private String formaterMontant(BigDecimal montant) {
        if (montant == null) {
            return "0 FCFA";
        }
        return String.format("%,.0f FCFA", montant);
    }

    private void telechargerFichier(byte[] data, String nomFichier, String contentType) {
        try {
            jakarta.faces.context.FacesContext fc = jakarta.faces.context.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) {
            LOG.errorf(e, "Erreur téléchargement fichier");
            throw new RuntimeException("Erreur lors du téléchargement", e);
        }
    }

    // ═══════════════════════════════════════════════════════════════════════
    // Getters / Setters
    // ═══════════════════════════════════════════════════════════════════════

    public UUID getMembreId() { return membreId; }
    public String getNumeroMembre() { return numeroMembre; }
    public MembreResponse getMembre() { return membre; }

    public Integer getCotisationsEnAttente() { return cotisationsEnAttente; }
    public String getMontantDu() { return montantDu; }
    public String getProchaineEcheance() { return prochaineEcheance; }
    public String getTotalPaye() { return totalPaye; }
    public Integer getAnneeEnCours() { return anneeEnCours; }

    public List<CotisationPerso> getMesCotisationsEnAttente() { return mesCotisationsEnAttente; }
    public List<PaiementPerso> getDerniersPaiements() { return derniersPaiements; }

    public UUID getCotisationSelectionneeId() { return cotisationSelectionneeId; }
    public void setCotisationSelectionneeId(UUID cotisationSelectionneeId) { this.cotisationSelectionneeId = cotisationSelectionneeId; }

    public CotisationPerso getCotisationSelectionnee() { return cotisationSelectionnee; }

    public String getMethodePaiementChoisie() { return methodePaiementChoisie; }
    public void setMethodePaiementChoisie(String methodePaiementChoisie) { this.methodePaiementChoisie = methodePaiementChoisie; }

    public String getNumeroTelephone() { return numeroTelephone; }
    public void setNumeroTelephone(String numeroTelephone) { this.numeroTelephone = numeroTelephone; }

    public String getMethodePaiementManuel() { return methodePaiementManuel; }
    public void setMethodePaiementManuel(String methodePaiementManuel) { this.methodePaiementManuel = methodePaiementManuel; }

    public String getReferencePaiementManuel() { return referencePaiementManuel; }
    public void setReferencePaiementManuel(String referencePaiementManuel) { this.referencePaiementManuel = referencePaiementManuel; }

    public String getCommentairePaiement() { return commentairePaiement; }
    public void setCommentairePaiement(String commentairePaiement) { this.commentairePaiement = commentairePaiement; }

    public boolean isPaiementManuelActive() { return paiementManuelActive; }

    // ═══════════════════════════════════════════════════════════════════════
    // DTOs internes
    // ═══════════════════════════════════════════════════════════════════════

    public static class CotisationPerso implements Serializable {
        private static final long serialVersionUID = 1L;

        private UUID id;
        private String reference;
        private String type;
        private String periode;
        private BigDecimal montantDu;
        private LocalDate dateEcheance;

        // Getters / Setters
        public UUID getId() { return id; }
        public void setId(UUID id) { this.id = id; }

        public String getReference() { return reference; }
        public void setReference(String reference) { this.reference = reference; }

        public String getType() { return type; }
        public void setType(String type) { this.type = type; }

        public String getPeriode() { return periode; }
        public void setPeriode(String periode) { this.periode = periode; }

        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; }

        // Méthodes dérivées pour l'affichage
        public String getTypeSeverity() {
            return switch (type) {
                case "MENSUELLE" -> "info";
                case "SPECIALE" -> "warning";
                case "ADHESION" -> "success";
                default -> "secondary";
            };
        }

        public String getTypeIcon() {
            return switch (type) {
                case "MENSUELLE" -> "pi-calendar";
                case "SPECIALE" -> "pi-star";
                case "ADHESION" -> "pi-user-plus";
                default -> "pi-circle";
            };
        }

        public String getMontantDuFormatte() {
            return montantDu != null ? String.format("%,.0f FCFA", montantDu) : "0 FCFA";
        }

        public String getDateEcheanceFormattee() {
            return dateEcheance != null ? dateEcheance.format(DATE_FORMATTER) : "-";
        }

        public String getTypeLibelle() {
            if (type == null) return "Cotisation";
            return switch (type) {
                case "MENSUELLE" -> "Mensuelle";
                case "SPECIALE" -> "Spéciale";
                case "ADHESION" -> "Adhésion";
                case "ANNUELLE" -> "Annuelle";
                default -> type;
            };
        }

        public String getLibelle() {
            return getTypeLibelle() + (periode != null && !periode.isBlank() ? " — " + periode : "");
        }

        public String getPeriodeFormatee() {
            return periode != null ? periode : "-";
        }

        public String getRetardColor() {
            if (dateEcheance == null) return "text-600";
            long jours = java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), dateEcheance);
            if (jours < 0) return "text-red-600 font-bold";
            if (jours <= 7) return "text-orange-600";
            return "text-600";
        }

        public String getStatutEcheance() {
            if (dateEcheance == null) return "-";
            long jours = java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), dateEcheance);
            if (jours < 0) return "En retard (" + Math.abs(jours) + " j)";
            if (jours == 0) return "Aujourd'hui";
            if (jours == 1) return "Demain";
            return "Dans " + jours + " jours";
        }
    }

    public static class PaiementPerso implements Serializable {
        private static final long serialVersionUID = 1L;

        private UUID id;
        private String reference;
        private String periode;
        private BigDecimal montant;
        private LocalDate datePaiement;
        private String methodePaiement;

        // Getters / Setters
        public UUID getId() { return id; }
        public void setId(UUID id) { this.id = id; }

        public String getReference() { return reference; }
        public void setReference(String reference) { this.reference = reference; }

        public String getPeriode() { return periode; }
        public void setPeriode(String periode) { this.periode = periode; }

        public BigDecimal getMontant() { return montant; }
        public void setMontant(BigDecimal montant) { this.montant = montant; }

        public LocalDate getDatePaiement() { return datePaiement; }
        public void setDatePaiement(LocalDate datePaiement) { this.datePaiement = datePaiement; }

        public String getMethodePaiement() { return methodePaiement; }
        public void setMethodePaiement(String methodePaiement) { this.methodePaiement = methodePaiement; }

        // Méthodes dérivées pour l'affichage
        public String getMontantFormatte() {
            return montant != null ? String.format("%,.0f FCFA", montant) : "0 FCFA";
        }

        public String getDatePaiementFormattee() {
            return datePaiement != null ? datePaiement.format(DATE_FORMATTER) : "-";
        }

        public String getTypeLibelle() {
            return periode != null && !periode.isBlank() ? periode : "Cotisation";
        }

        public String getMethodeSeverity() {
            if (methodePaiement == null) return "secondary";
            return switch (methodePaiement) {
                case "WAVE_MONEY" -> "success";
                case "ORANGE_MONEY" -> "warning";
                case "FREE_MONEY" -> "info";
                case "CARTE_BANCAIRE" -> "primary";
                case "ESPECES" -> "secondary";
                case "VIREMENT" -> "info";
                case "CHEQUE" -> "secondary";
                default -> "secondary";
            };
        }

        public String getMethodeIcon() {
            if (methodePaiement == null) return "pi-credit-card";
            return switch (methodePaiement) {
                case "WAVE_MONEY" -> "pi-mobile";
                case "ORANGE_MONEY" -> "pi-mobile";
                case "FREE_MONEY" -> "pi-mobile";
                case "CARTE_BANCAIRE" -> "pi-credit-card";
                case "ESPECES" -> "pi-money-bill";
                case "VIREMENT" -> "pi-building";
                case "CHEQUE" -> "pi-file";
                default -> "pi-credit-card";
            };
        }
    }
}
