package dev.lions.unionflow.client.view;

import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.client.service.OrganisationService;
import dev.lions.unionflow.client.service.ErrorHandlerService;
import dev.lions.unionflow.client.service.RetryService;
import dev.lions.unionflow.server.api.dto.organisation.request.*;
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
import dev.lions.unionflow.server.api.dto.organisation.response.*;
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
import dev.lions.unionflow.server.api.dto.organisation.response.OrganisationResponse;
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
import lombok.Getter;
import lombok.Setter;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import jakarta.inject.Inject;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;
import java.io.IOException;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Named("membreExportBean")
@ViewScoped
@Getter
@Setter
public class MembreExportBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(MembreExportBean.class);

    @Inject
    @RestClient
    MembreService membreService;

    @Inject
    @RestClient
    OrganisationService organisationService;

    @Inject
    ErrorHandlerService errorHandler;

    @Inject
    RetryService retryService;

    // Configuration de l'export
    private String formatExport = "EXCEL";
    private String scopeExport = "TOUS";
    private List<String> colonnesExport = new ArrayList<>();

    // Filtres
    private String statutFilter = "";
    private String typeFilter = "";
    private UUID organisationId;
    private LocalDate dateAdhesionDebut;
    private LocalDate dateAdhesionFin;

    // Options d'export
    private boolean inclureHeaders = true;
    private boolean formaterDates = true;
    private boolean inclureStatistiques = false;
    private boolean chiffrerDonnees = false;
    private String motDePasseExport = "";

    // Organisations disponibles
    private List<OrganisationResponse> organisationsDisponibles = new ArrayList<>();

    // Statistiques
    private int totalMembres = 0;
    private int membresActifs = 0;
    private int membresInactifs = 0;
    private int nombreMembresAExporter = 0;

    // Historique des exports
    private List<ExportHistorique> historiqueExports = new ArrayList<>();

    @PostConstruct
    public void init() {
        chargerOrganisations();
        chargerStatistiques();
        initialiserColonnesExport();
    }

    private void chargerOrganisations() {
        organisationsDisponibles = new ArrayList<>();
        try {
            PagedResponse<OrganisationSummaryResponse> response = retryService.executeWithRetrySupplier(
                    () -> organisationService.listerToutes(0, 1000),
                    "chargement des organisations pour export");
            List<OrganisationSummaryResponse> associations = (response != null && response.getData() != null) ? response.getData()
                    : new ArrayList<>();
            for (OrganisationSummaryResponse assoc : associations) {
                OrganisationResponse org = new OrganisationResponse();
                org.setId(assoc.getId());
                org.setNom(assoc.getNom());
                org.setVille(null /* ville not in Summary */);
                organisationsDisponibles.add(org);
            }
            LOG.infof("Organisations chargées: %d organisations", organisationsDisponibles.size());
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des organisations");
            errorHandler.handleException(e, "lors du chargement des organisations", null);
        }
    }

    private void chargerStatistiques() {
        try {
            // Note: obtenirStatistiques() retourne probablement Map<String, Object> ou un autre type
            // Pour l'instant on désactive cette fonctionnalité jusqu'à clarification du type de retour
            /*
            var stats = retryService.executeWithRetrySupplier(
                    () -> membreService.obtenirStatistiques(),
                    "chargement des statistiques pour export");
            if (stats != null) {
                totalMembres = stats.getTotalMembres() != null ? stats.getTotalMembres().intValue() : 0;
                membresActifs = stats.getMembresActifs() != null ? stats.getMembresActifs().intValue() : 0;
                membresInactifs = stats.getMembresInactifs() != null ? stats.getMembresInactifs().intValue() : 0;
            }
            */
            totalMembres = 0;
            membresActifs = 0;
            membresInactifs = 0;
            actualiserCompteur();
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des statistiques");
            errorHandler.handleException(e, "lors du chargement des statistiques", null);
        }
    }

    private void initialiserColonnesExport() {
        colonnesExport = new ArrayList<>();
        colonnesExport.add("PERSO");
        colonnesExport.add("CONTACT");
        colonnesExport.add("ADHESION");
    }

    public void actualiserCompteur() {
        actualiserCompteur(null);
    }

    public void actualiserCompteur(jakarta.faces.event.AjaxBehaviorEvent event) {
        try {
            // Appel au backend pour obtenir le comptage exact selon les filtres
            String statut = null;
            if ("ACTIFS".equals(scopeExport)) {
                statut = "ACTIF";
            } else if ("INACTIFS".equals(scopeExport)) {
                statut = "INACTIF";
            } else if (statutFilter != null && !statutFilter.isEmpty()) {
                statut = statutFilter;
            }

            String dateAdhesionDebutStr = dateAdhesionDebut != null ? dateAdhesionDebut.toString() : null;
            String dateAdhesionFinStr = dateAdhesionFin != null ? dateAdhesionFin.toString() : null;

            Long count = membreService.compterMembresPourExport(
                    organisationId,
                    statut,
                    typeFilter != null && !typeFilter.isEmpty() ? typeFilter : null,
                    dateAdhesionDebutStr,
                    dateAdhesionFinStr);

            nombreMembresAExporter = count != null ? count.intValue() : 0;

            LOG.infof("Comptage des membres pour export: %d", nombreMembresAExporter);

        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de l'actualisation du compteur");
            errorHandler.handleException(e, "lors de l'actualisation du compteur", null);
            // Fallback sur estimation basée sur les statistiques
            if ("TOUS".equals(scopeExport)) {
                nombreMembresAExporter = totalMembres;
            } else if ("ACTIFS".equals(scopeExport)) {
                nombreMembresAExporter = membresActifs;
            } else if ("INACTIFS".equals(scopeExport)) {
                nombreMembresAExporter = membresInactifs;
            } else {
                nombreMembresAExporter = 0;
            }
        }
    }

    public void exporterMembres() {
        if (colonnesExport == null || colonnesExport.isEmpty()) {
            errorHandler.showWarning("Attention",
                    "Veuillez sélectionner au moins une catégorie de colonnes à exporter");
            return;
        }

        if (nombreMembresAExporter == 0) {
            errorHandler.showWarning("Attention",
                    "Aucun membre ne correspond aux critères sélectionnés");
            return;
        }

        try {
            LOG.infof("Export des membres: format=%s, nombre=%d", formatExport, nombreMembresAExporter);

            String dateAdhesionDebutStr = dateAdhesionDebut != null ? dateAdhesionDebut.toString() : null;
            String dateAdhesionFinStr = dateAdhesionFin != null ? dateAdhesionFin.toString() : null;

            // Générer un mot de passe aléatoire si le chiffrement est demandé
            final String motDePasseFinal;
            if (chiffrerDonnees) {
                if (motDePasseExport != null && !motDePasseExport.trim().isEmpty()) {
                    motDePasseFinal = motDePasseExport;
                } else {
                    // Générer un mot de passe aléatoire de 12 caractères
                    motDePasseFinal = genererMotDePasseAleatoire();
                }
            } else {
                motDePasseFinal = null;
            }

            byte[] exportData = retryService.executeWithRetrySupplier(
                    () -> membreService.exporterExcel(
                            formatExport,
                            organisationId,
                            statutFilter,
                            typeFilter,
                            dateAdhesionDebutStr,
                            dateAdhesionFinStr,
                            colonnesExport,
                            inclureHeaders,
                            formaterDates,
                            inclureStatistiques && "EXCEL".equals(formatExport), // Statistiques uniquement pour Excel
                            motDePasseFinal),
                    "export de membres");

            jakarta.faces.context.FacesContext facesContext = jakarta.faces.context.FacesContext.getCurrentInstance();
            HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();

            response.reset();
            String contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            String extension = "xlsx";

            if ("CSV".equals(formatExport)) {
                contentType = "text/csv";
                extension = "csv";
            }

            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment; filename=\"membres_export_" +
                    LocalDate.now() + "." + extension + "\"");
            response.setContentLength(exportData.length);

            response.getOutputStream().write(exportData);
            response.getOutputStream().flush();
            facesContext.responseComplete();

            // Ajouter à l'historique
            ExportHistorique historique = new ExportHistorique();
            historique.setDate(LocalDateTime.now());
            historique.setFormat(formatExport);
            historique.setNombreMembres(nombreMembresAExporter);
            historique.setTaille(formatTaille(exportData.length));
            historiqueExports.add(0, historique); // Ajouter au début

            // Afficher le mot de passe si le chiffrement était demandé
            if (chiffrerDonnees && motDePasseFinal != null) {
                errorHandler.showInfo("Fichier protégé",
                        "Le fichier a été protégé par un mot de passe. " +
                                "Mot de passe: " + motDePasseFinal +
                                " (Note: Le fichier est protégé contre la modification, mais peut toujours être ouvert)");
            }

            LOG.infof("Export généré et téléchargé: %d bytes", exportData.length);
        } catch (IOException e) {
            LOG.errorf(e, "Erreur lors du téléchargement de l'export");
            errorHandler.handleException(e, "lors du téléchargement de l'export", null);
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de l'export");
            errorHandler.handleException(e, "lors de l'export de membres", null);
        }
    }

    private String formatTaille(long bytes) {
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else {
            return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
        }
    }

    public void telechargerExport(ExportHistorique export) {
        // L'historique est stocké localement dans la session, pas de téléchargement
        // depuis le serveur
        LOG.infof("Export historique consulté: %s - %s", export.getDate(), export.getFormat());
        errorHandler.showInfo("Information",
                "L'export du "
                        + export.getDate().format(java.time.format.DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")) +
                        " n'est plus disponible. Veuillez générer un nouvel export.");
    }

    private String genererMotDePasseAleatoire() {
        String caracteres = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*";
        java.util.Random random = new java.util.Random();
        StringBuilder motDePasse = new StringBuilder(12);
        for (int i = 0; i < 12; i++) {
            motDePasse.append(caracteres.charAt(random.nextInt(caracteres.length())));
        }
        return motDePasse.toString();
    }

    public void reinitialiser() {
        formatExport = "EXCEL";
        scopeExport = "TOUS";
        colonnesExport = new ArrayList<>();
        colonnesExport.add("PERSO");
        colonnesExport.add("CONTACT");
        colonnesExport.add("ADHESION");
        statutFilter = "";
        typeFilter = "";
        organisationId = null;
        dateAdhesionDebut = null;
        dateAdhesionFin = null;
        inclureHeaders = true;
        formaterDates = true;
        inclureStatistiques = false;
        chiffrerDonnees = false;
        motDePasseExport = "";
        actualiserCompteur();
    }

    // Classe interne pour l'historique des exports
    public static class ExportHistorique implements Serializable {
        private LocalDateTime date;
        private String format;
        private int nombreMembres;
        private String taille;

        public LocalDateTime getDate() { return date; }
        public void setDate(LocalDateTime date) { this.date = date; }
        public String getFormat() { return format; }
        public void setFormat(String format) { this.format = format; }
        public int getNombreMembres() { return nombreMembres; }
        public void setNombreMembres(int nombreMembres) { this.nombreMembres = nombreMembres; }
        public String getTaille() { return taille; }
        public void setTaille(String taille) { this.taille = taille; }
    }
}
