package dev.lions.unionflow.client.view;

import dev.lions.unionflow.server.api.dto.reference.request.*;
import dev.lions.unionflow.server.api.dto.reference.response.*;
import dev.lions.unionflow.client.service.ErrorHandlerService;
import dev.lions.unionflow.client.service.RetryService;
import dev.lions.unionflow.client.service.TypeCatalogueService;
import dev.lions.unionflow.client.service.TypeOrganisationClientService;
import dev.lions.unionflow.client.service.RestClientExceptionMapper;
import jakarta.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;

/**
 * Bean de gestion du catalogue des types d'organisation (UI Super Admin).
 * 
 * <p>Intègre les meilleures pratiques de production :
 * <ul>
 *   <li>ErrorHandlerService pour la gestion centralisée des erreurs</li>
 *   <li>RetryService pour les retries automatiques</li>
 *   <li>Logging structuré avec JBoss Logging</li>
 * </ul>
 * 
 * @author UnionFlow Team
 * @version 2.0
 * @since 2025-12-24
 */
@Named("typeOrganisationsAdminBean")
@ViewScoped
public class TypeOrganisationsAdminBean implements Serializable {

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

    @Inject
    @RestClient
    TypeOrganisationClientService typeOrganisationClientService;
    
    @Inject
    ErrorHandlerService errorHandler;
    
    @Inject
    RetryService retryService;

    @Inject
    TypeCatalogueService typeCatalogueService;

    private static final List<String> CATEGORIES_DISPONIBLES = Arrays.asList(
        "ASSOCIATIF", "FINANCIER_SOLIDAIRE", "RELIGIEUX", "PROFESSIONNEL", "RESEAU_FEDERATION"
    );

    private static final List<String> MODULES_DISPONIBLES = Arrays.asList(
        "COTISATIONS", "EVENEMENTS", "SOLIDARITE", "COMPTABILITE", "DOCUMENTS",
        "NOTIFICATIONS", "CREDIT_EPARGNE", "AYANTS_DROIT", "TONTINE", "ONG_PROJETS",
        "COOP_AGRICOLE", "VOTE_INTERNE", "COLLECTE_FONDS", "REGISTRE_PROFESSIONNEL",
        "CULTES_RELIGIEUX", "GOUVERNANCE_MULTI"
    );

    private List<TypeReferenceResponse> types = new ArrayList<>();
    /** Type actuellement édité dans le dialogue (nouveau ou existant). */
    private TypeReferenceResponse typeCourant;
    private TypeReferenceResponse typeSelectionne;
    /** ID du type à supprimer (pour dialogue de confirmation explicite). */
    private UUID typeASupprimerId;
    /** Modules sélectionnés dans le dialogue (synchronisé avec typeCourant.modulesRequis CSV). */
    private List<String> modulesSelectionnes = new ArrayList<>();

    @PostConstruct
    public void init() {
        chargerTypes();
    }

    public void chargerTypes() {
        try {
            types = retryService.executeWithRetrySupplier(
                () -> typeOrganisationClientService.list(false),
                "chargement des types d'organisation"
            );
            LOG.infof("Types d'organisation chargés: %d types", types.size());
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des types d'organisation");
            errorHandler.handleException(e, "lors du chargement des types d'organisation", null);
            types = new ArrayList<>();
        }
    }

    public void preparerNouveauType() {
        typeCourant = new TypeReferenceResponse();
        typeCourant.setActif(true);
        typeSelectionne = null;
        modulesSelectionnes = new ArrayList<>();
    }

    private void creerType() {
        if (typeCourant == null) {
            errorHandler.showWarning("Erreur", "Aucun type à créer");
            return;
        }

        try {
            LOG.infof("Tentative de création d'un type d'organisation: %s", typeCourant);

            String modulesRequisCsv = modulesSelectionnes.isEmpty()
                ? null : String.join(",", modulesSelectionnes);

            CreateTypeReferenceRequest request = CreateTypeReferenceRequest.builder()
                .domaine("TYPE_ORGANISATION")
                .code(typeCourant.getCode())
                .libelle(typeCourant.getLibelle())
                .description(typeCourant.getDescription())
                .ordreAffichage(typeCourant.getOrdreAffichage() != null ? typeCourant.getOrdreAffichage() : 0)
                .estDefaut(false)
                .estSysteme(false)
                .organisationId(null)
                .categorie(typeCourant.getCategorie())
                .modulesRequis(modulesRequisCsv)
                .build();

            TypeReferenceResponse cree = retryService.executeWithRetrySupplier(
                () -> typeOrganisationClientService.create(request),
                "création d'un type d'organisation"
            );

            LOG.infof("Type d'organisation créé avec succès: id=%s", cree.getId());
            typeCourant = null;
            typeCatalogueService.recharger();
            chargerTypes();

            errorHandler.showSuccess("Succès", "Type d'organisation créé avec succès");

        } catch (RestClientExceptionMapper.UnauthorizedException e) {
            LOG.errorf(e, "Erreur 401 - Non autorisé lors de la création");
            errorHandler.handleException(e, "lors de la création d'un type d'organisation",
                "Vous n'êtes pas autorisé à créer un type d'organisation. Vérifiez vos permissions.");
        } catch (RestClientExceptionMapper.ForbiddenException e) {
            LOG.errorf(e, "Erreur 403 - Accès interdit lors de la création");
            errorHandler.handleException(e, "lors de la création d'un type d'organisation",
                "Vous n'avez pas les permissions nécessaires. Contactez un administrateur.");
        } catch (jakarta.ws.rs.ProcessingException e) {
            LOG.errorf(e, "Erreur de connexion au backend");
            errorHandler.handleException(e, "lors de la création d'un type d'organisation",
                "Impossible de se connecter au serveur. Vérifiez que le backend est démarré.");
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de la création du type d'organisation");
            errorHandler.handleException(e, "lors de la création d'un type d'organisation", null);
        }
    }

    /**
     * Méthode unique utilisée par le bouton "Enregistrer" du dialogue.
     * Si un nouveau type est en cours d'édition, on crée, sinon on met à jour le type sélectionné.
     */
    public void enregistrerType() {
        if (typeCourant == null) {
            return;
        }
        if (typeCourant.getId() == null) {
            creerType();
        } else {
            sauvegarderType();
        }
    }
    
    private void sauvegarderType() {
        if (typeCourant == null || typeCourant.getId() == null) {
            errorHandler.showWarning("Erreur", "Aucun type sélectionné pour modification");
            return;
        }

        try {
            String modulesRequisCsv = modulesSelectionnes.isEmpty()
                ? null : String.join(",", modulesSelectionnes);

            UpdateTypeReferenceRequest request = new UpdateTypeReferenceRequest(
                typeCourant.getCode(),
                typeCourant.getLibelle(),
                typeCourant.getDescription(),
                null, // icone
                null, // couleur
                null, // severity
                typeCourant.getOrdreAffichage(),
                null, // estDefaut
                typeCourant.getActif(),
                typeCourant.getCategorie(),
                modulesRequisCsv
            );

            TypeReferenceResponse maj = retryService.executeWithRetrySupplier(
                () -> typeOrganisationClientService.update(typeCourant.getId(), request),
                "mise à jour d'un type d'organisation"
            );

            LOG.infof("Type d'organisation mis à jour avec succès: id=%s", maj.getId());
            typeCourant = null;
            typeCatalogueService.recharger();
            chargerTypes();

            errorHandler.showSuccess("Succès", "Type d'organisation mis à jour");

        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de la mise à jour du type d'organisation");
            errorHandler.handleException(e, "lors de la mise à jour d'un type d'organisation", null);
        }
    }

    /**
     * Ouvre le dialogue de confirmation de suppression (stocke l'id).
     */
    public void preparerSuppression(UUID id) {
        typeASupprimerId = id;
    }

    /**
     * Confirme et exécute la suppression du type dont l'id a été stocké par preparerSuppression.
     */
    public void confirmerSuppression() {
        if (typeASupprimerId == null) {
            errorHandler.showWarning("Erreur", "Aucun type à supprimer");
            return;
        }
        UUID id = typeASupprimerId;
        typeASupprimerId = null;
        supprimerType(id);
    }

    /**
     * Supprime définitivement un type d'organisation (appel DELETE côté API).
     */
    public void supprimerType(UUID id) {
        if (id == null) {
            errorHandler.showWarning("Erreur", "Aucun type sélectionné");
            return;
        }
        try {
            retryService.executeWithRetrySupplier(
                () -> {
                    typeOrganisationClientService.disable(id);
                    return null;
                },
                "suppression d'un type d'organisation"
            );
            LOG.infof("Type d'organisation supprimé avec succès: id=%s", id);
            typeCatalogueService.recharger();
            chargerTypes();
            errorHandler.showSuccess("Succès", "Type d'organisation supprimé");
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de la suppression du type d'organisation");
            errorHandler.handleException(e, "lors de la suppression d'un type d'organisation", null);
        }
    }

    // Getters / Setters
    public List<TypeReferenceResponse> getTypes() { 
        return types; 
    }
    
    public void setTypes(List<TypeReferenceResponse> types) { 
        this.types = types; 
    }

    public TypeReferenceResponse getTypeSelectionne() { 
        return typeSelectionne; 
    }
    
    public void setTypeSelectionne(TypeReferenceResponse typeSelectionne) {
        this.typeSelectionne = typeSelectionne;
        this.typeCourant = typeSelectionne;
        // Reconvertir le CSV en liste pour les checkboxes
        String csv = (typeSelectionne != null) ? typeSelectionne.getModulesRequis() : null;
        modulesSelectionnes = (csv != null && !csv.isBlank())
            ? new ArrayList<>(Arrays.asList(csv.split(",")))
            : new ArrayList<>();
    }

    /**
     * Retourne le type actuellement édité dans le dialogue.
     * Initialise un nouveau type par défaut si aucun n'est encore défini,
     * ce qui évite les erreurs "Target Unreachable" lors de la validation JSF.
     */
    public TypeReferenceResponse getTypeCourant() {
        if (typeCourant == null) {
            typeCourant = new TypeReferenceResponse();
            typeCourant.setActif(true);
        }
        return typeCourant;
    }
    
    public void setTypeCourant(TypeReferenceResponse typeCourant) {
        this.typeCourant = typeCourant;
    }

    public List<String> getModulesSelectionnes() {
        return modulesSelectionnes;
    }

    public void setModulesSelectionnes(List<String> modulesSelectionnes) {
        this.modulesSelectionnes = modulesSelectionnes;
    }

    public List<String> getCategoriesDisponibles() {
        return CATEGORIES_DISPONIBLES;
    }

    public List<String> getModulesDisponibles() {
        return MODULES_DISPONIBLES;
    }
}
