package dev.lions.unionflow.client.view;

import dev.lions.unionflow.client.constants.StatutOrganisationConstants;
import dev.lions.unionflow.client.dto.AssociationDTO;
import dev.lions.unionflow.client.dto.TypeOrganisationClientDTO;
import dev.lions.unionflow.client.service.AssociationService;
import dev.lions.unionflow.client.service.TypeOrganisationClientService;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import jakarta.faces.model.SelectItem;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * Bean de gestion des organisations
 */
@Named("organisationsBean")
@ViewScoped
public class OrganisationsBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = Logger.getLogger(OrganisationsBean.class.getName());

    @Inject
    @RestClient
    AssociationService associationService;

    @Inject
    @RestClient
    TypeOrganisationClientService typeOrganisationClientService;

    // Liste des organisations
    private List<AssociationDTO> organisations = new ArrayList<>();
    private List<AssociationDTO> organisationsFiltrees;
    
    // Organisation sélectionnée ou en cours de création/modification
    private AssociationDTO organisationSelectionnee;
    private AssociationDTO nouvelleOrganisation;
    
    // Statistiques
    private long totalOrganisations;
    private long organisationsActives;
    private long organisationsInactives;
    
    // Filtres
    private String rechercheGlobale;
    private String filtreStatut;
    private String filtreType;
    // Catalogue des types pour la liste déroulante
    private List<TypeOrganisationClientDTO> typesCatalogue = new ArrayList<>();
    private String filtreRegion;
    
    @PostConstruct
    public void init() {
        chargerOrganisations();
        chargerStatistiques();
        chargerTypesOrganisation();
    }

    public void chargerOrganisations() {
        try {
            organisations = associationService.listerToutes(0, 1000);
            organisationsFiltrees = organisations;
            LOGGER.info("Chargement de " + organisations.size() + " organisations");
        } catch (Exception e) {
            LOGGER.severe("Erreur lors du chargement des organisations: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible de charger les organisations: " + e.getMessage()));
            organisations = new ArrayList<>();
            organisationsFiltrees = new ArrayList<>();
        }
    }

    public void chargerTypesOrganisation() {
        try {
            typesCatalogue = typeOrganisationClientService.list(true);
        } catch (Exception e) {
            LOGGER.severe("Impossible de charger le catalogue des types d'organisation: " + e.getMessage());
            typesCatalogue = new ArrayList<>();
        }
    }

    public void chargerStatistiques() {
        try {
            AssociationService.StatistiquesAssociationDTO stats = associationService.obtenirStatistiques();
            if (stats != null) {
                totalOrganisations = stats.getTotalAssociations() != null ? stats.getTotalAssociations() : 0L;
                organisationsActives = stats.getAssociationsActives() != null ? stats.getAssociationsActives() : 0L;
                organisationsInactives = stats.getAssociationsInactives() != null ? stats.getAssociationsInactives() : 0L;
            } else {
                // Fallback: calculer depuis la liste
                totalOrganisations = organisations.size();
                organisationsActives = organisations.stream()
                    .filter(o -> o.getStatut() != null && "ACTIVE".equals(o.getStatut()))
                    .count();
                organisationsInactives = totalOrganisations - organisationsActives;
            }
        } catch (dev.lions.unionflow.client.service.RestClientExceptionMapper.UnauthorizedException e) {
            // Non bloquant: afficher une info et calculer depuis la liste
            LOGGER.warning("Statistiques non autorisées (401): " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "Information",
                    "Statistiques indisponibles (non autorisé) — affichage des données sans stats."));
            totalOrganisations = organisations.size();
            organisationsActives = organisations.stream()
                .filter(o -> o.getStatut() != null && StatutOrganisationConstants.ACTIVE.equals(o.getStatut()))
                .count();
            organisationsInactives = totalOrganisations - organisationsActives;
        } catch (Exception e) {
            LOGGER.warning("Impossible de charger les statistiques: " + e.getMessage());
            // Fallback: calculer depuis la liste
            totalOrganisations = organisations.size();
            organisationsActives = organisations.stream()
                .filter(o -> o.getStatut() != null && StatutOrganisationConstants.ACTIVE.equals(o.getStatut()))
                .count();
            organisationsInactives = totalOrganisations - organisationsActives;
        }
    }

    public void preparerNouvelleOrganisation() {
        nouvelleOrganisation = new AssociationDTO();
        nouvelleOrganisation.setStatut(StatutOrganisationConstants.ACTIVE);

        // S'assurer que le catalogue des types est chargé avant d'initialiser le formulaire
        if (typesCatalogue == null || typesCatalogue.isEmpty()) {
            chargerTypesOrganisation();
        }

        // Déterminer un type par défaut dynamique (premier type actif du catalogue)
        String typeDefaut = null;
        if (typesCatalogue != null) {
            typeDefaut = typesCatalogue.stream()
                .filter(t -> t.getActif() == null || Boolean.TRUE.equals(t.getActif()))
                .map(TypeOrganisationClientDTO::getCode)
                .findFirst()
                .orElse(null);
        }
        nouvelleOrganisation.setTypeAssociation(typeDefaut);
        nouvelleOrganisation.setDateFondation(java.time.LocalDate.now());
    }

    public void creerOrganisation() {
        try {
            AssociationDTO creee = associationService.creer(nouvelleOrganisation);
            organisations.add(0, creee);
            organisationsFiltrees = organisations;
            
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "Succès", "Organisation '" + creee.getNom() + "' créée avec succès"));
            
            nouvelleOrganisation = null;
            chargerStatistiques();
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la création: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible de créer l'organisation: " + e.getMessage()));
        }
    }

    public void modifierOrganisation() {
        try {
            AssociationDTO modifiee = associationService.modifier(
                organisationSelectionnee.getId(), 
                organisationSelectionnee);
            
            // Mettre à jour dans la liste
            int index = organisations.indexOf(organisationSelectionnee);
            if (index >= 0) {
                organisations.set(index, modifiee);
                organisationsFiltrees = organisations;
            }
            
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "Succès", "Organisation modifiée avec succès"));
            
            organisationSelectionnee = null;
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la modification: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible de modifier l'organisation: " + e.getMessage()));
        }
    }

    public void supprimerOrganisation(AssociationDTO organisation) {
        try {
            associationService.supprimer(organisation.getId());
            organisations.remove(organisation);
            organisationsFiltrees = organisations;
            
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "Succès", "Organisation supprimée avec succès"));
            
            chargerStatistiques();
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la suppression: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible de supprimer l'organisation: " + e.getMessage()));
        }
    }

    public void activerOrganisation(AssociationDTO organisation) {
        try {
            associationService.activer(organisation.getId());
            organisation.setStatut(StatutOrganisationConstants.ACTIVE);
            
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "Succès", "Organisation activée"));
            
            chargerStatistiques();
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de l'activation: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible d'activer l'organisation"));
        }
    }

    public void desactiverOrganisation(AssociationDTO organisation) {
        try {
            associationService.suspendre(organisation.getId());
            organisation.setStatut(StatutOrganisationConstants.INACTIVE);
            
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "Succès", "Organisation désactivée"));
            
            chargerStatistiques();
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la désactivation: " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible de désactiver l'organisation"));
        }
    }

    /**
     * Recherche les organisations dont le nom contient la requête fournie.
     * Méthode utilitaire côté client qui délègue au service REST backend.
     *
     * @param query terme de recherche (partie du nom)
     * @return liste d'organisations correspondant au critère, ou liste vide en cas d'erreur
     */
    public List<AssociationDTO> rechercherOrganisations(String query) {
        if (query == null || query.trim().isEmpty()) {
            return organisations; // rien saisi : on renvoie la liste actuelle
        }
        try {
            // On délègue au endpoint /api/organisations/recherche avec uniquement le nom rempli.
            List<AssociationDTO> resultats = associationService.rechercher(
                query,          // nom
                null,           // type
                null,           // statut
                null,           // region
                null,           // ville
                0,              // page
                100             // size
            );
            LOGGER.info("Recherche d'organisations pour '" + query + "': " +
                (resultats != null ? resultats.size() : 0) + " résultat(s)");
            return resultats != null ? resultats : List.of();
        } catch (Exception e) {
            LOGGER.severe("Erreur lors de la recherche d'organisations pour '" + query + "': " + e.getMessage());
            FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Erreur", "Impossible de rechercher les organisations: " + e.getMessage()));
            return List.of();
        }
    }

    /**
     * Bascule le statut d'une organisation entre ACTIVE et INACTIVE
     * Cette méthode est utilisée pour éviter l'utilisation d'expressions ternaires dans les expressions EL
     */
    public void basculerStatutOrganisation(AssociationDTO organisation) {
        if (organisation == null || organisation.getStatut() == null) {
            return;
        }
        
        String statutActuel = organisation.getStatut();
        if (StatutOrganisationConstants.ACTIVE.equals(statutActuel)) {
            desactiverOrganisation(organisation);
        } else {
            activerOrganisation(organisation);
        }
    }

    public void appliquerFiltres() {
        organisationsFiltrees = organisations.stream()
            .filter(o -> {
                boolean match = true;
                
                if (rechercheGlobale != null && !rechercheGlobale.trim().isEmpty()) {
                    String recherche = rechercheGlobale.toLowerCase();
                    match = o.getNom().toLowerCase().contains(recherche) ||
                           (o.getVille() != null && o.getVille().toLowerCase().contains(recherche)) ||
                           (o.getDescription() != null && o.getDescription().toLowerCase().contains(recherche));
                }
                
                if (match && filtreStatut != null && !filtreStatut.isEmpty()) {
                    match = filtreStatut.equals(o.getStatut());
                }
                
                if (match && filtreType != null && !filtreType.isEmpty()) {
                    match = filtreType.equals(o.getTypeAssociation());
                }
                
                if (match && filtreRegion != null && !filtreRegion.isEmpty()) {
                    match = filtreRegion.equals(o.getRegion());
                }
                
                return match;
            })
            .toList();
    }

    public void reinitialiserFiltres() {
        rechercheGlobale = null;
        filtreStatut = null;
        filtreType = null;
        filtreRegion = null;
        organisationsFiltrees = organisations;
    }

    /**
     * Recharge la liste et les statistiques (DRY)
     */
    public void recharger() {
        chargerOrganisations();
        chargerStatistiques();
    }

    // Getters & Setters
    public List<AssociationDTO> getOrganisations() { return organisations; }
    public void setOrganisations(List<AssociationDTO> organisations) { this.organisations = organisations; }

    public List<AssociationDTO> getOrganisationsFiltrees() { return organisationsFiltrees; }
    public void setOrganisationsFiltrees(List<AssociationDTO> organisationsFiltrees) { this.organisationsFiltrees = organisationsFiltrees; }

    public AssociationDTO getOrganisationSelectionnee() { return organisationSelectionnee; }
    public void setOrganisationSelectionnee(AssociationDTO organisationSelectionnee) { this.organisationSelectionnee = organisationSelectionnee; }

    public AssociationDTO getNouvelleOrganisation() { return nouvelleOrganisation; }
    public void setNouvelleOrganisation(AssociationDTO nouvelleOrganisation) { this.nouvelleOrganisation = nouvelleOrganisation; }

    public long getTotalOrganisations() { return totalOrganisations; }
    public long getOrganisationsActives() { return organisationsActives; }
    public long getOrganisationsInactives() { return organisationsInactives; }

    public String getRechercheGlobale() { return rechercheGlobale; }
    public void setRechercheGlobale(String rechercheGlobale) { this.rechercheGlobale = rechercheGlobale; }

    public String getFiltreStatut() { return filtreStatut; }
    public void setFiltreStatut(String filtreStatut) { this.filtreStatut = filtreStatut; }

    public String getFiltreType() { return filtreType; }
    public void setFiltreType(String filtreType) { this.filtreType = filtreType; }

    public String getFiltreRegion() { return filtreRegion; }
    public void setFiltreRegion(String filtreRegion) { this.filtreRegion = filtreRegion; }

    // Méthodes utilitaires pour les statuts
    public boolean estActive(AssociationDTO organisation) {
        return organisation != null && 
               organisation.getStatut() != null && 
               StatutOrganisationConstants.ACTIVE.equals(organisation.getStatut());
    }

    public String getStatutActive() {
        return StatutOrganisationConstants.ACTIVE;
    }

    public String getStatutInactive() {
        return StatutOrganisationConstants.INACTIVE;
    }

    public String getStatutSuspendue() {
        return StatutOrganisationConstants.SUSPENDUE;
    }

    public String getStatutDissoute() {
        return StatutOrganisationConstants.DISSOUTE;
    }

    /**
     * Retourne la liste des statuts pour les SelectItem (DRY/WOU)
     */
    public List<SelectItem> getStatutsSelectItems() {
        List<SelectItem> items = new ArrayList<>();
        items.add(new SelectItem("", "Tous les statuts"));
        items.add(new SelectItem(StatutOrganisationConstants.ACTIVE, "Active"));
        items.add(new SelectItem(StatutOrganisationConstants.INACTIVE, "Inactive"));
        items.add(new SelectItem(StatutOrganisationConstants.SUSPENDUE, "Suspendue"));
        items.add(new SelectItem(StatutOrganisationConstants.DISSOUTE, "Dissoute"));
        return items;
    }

    /**
     * Retourne la liste des types d'organisation pour les SelectItem (DRY/WOU)
     */
    public List<SelectItem> getTypesSelectItems() {
        List<SelectItem> items = new ArrayList<>();
        items.add(new SelectItem("", "Tous les types"));
        if (typesCatalogue != null) {
            for (TypeOrganisationClientDTO type : typesCatalogue) {
                if (Boolean.FALSE.equals(type.getActif())) {
                    continue;
                }
                items.add(new SelectItem(type.getCode(), type.getLibelle()));
            }
        }
        return items;
    }

    /**
     * Retourne la liste des types d'organisation pour les formulaires (sans "Tous les types")
     */
    public List<SelectItem> getTypesSelectItemsForForm() {
        List<SelectItem> items = new ArrayList<>();
        items.add(new SelectItem("", "Sélectionner..."));
        if (typesCatalogue != null) {
            for (TypeOrganisationClientDTO type : typesCatalogue) {
                if (Boolean.FALSE.equals(type.getActif())) {
                    continue;
                }
                items.add(new SelectItem(type.getCode(), type.getLibelle()));
            }
        }
        return items;
    }
}

