package dev.lions.config;

import dev.lions.exceptions.ConfigurationException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.faces.annotation.FacesConfig;
import jakarta.faces.application.ViewHandler;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.PostConstructApplicationEvent;
import jakarta.faces.event.PreDestroyApplicationEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.util.Map;

/**
 * Configuration Jakarta Server Faces (JSF) de l'application.
 * Cette classe gère l'ensemble des paramètres et comportements spécifiques à JSF,
 * assurant une expérience utilisateur cohérente et performante.
 *
 * @author Lions Dev Team
 * @version 2.1
 */
@Slf4j
@ApplicationScoped
@FacesConfig
public class JSFConfiguration {

  @Inject
  @ConfigProperty(name = "jakarta.faces.PROJECT_STAGE", defaultValue = "Development")
  String projectStage;

  @Inject
  @ConfigProperty(name = "jakarta.faces.FACELETS_REFRESH_PERIOD", defaultValue = "2")
  Integer faceletsRefreshPeriod;

  @Inject
  @ConfigProperty(name = "jakarta.faces.STATE_SAVING_METHOD", defaultValue = "server")
  String stateSavingMethod;

  @Inject
  @ConfigProperty(name = "primefaces.THEME", defaultValue = "saga")
  private String primefacesTheme;

  @Inject
  @ConfigProperty(name = "jakarta.faces.VALIDATE_EMPTY_FIELDS", defaultValue = "true")
  private Boolean validateEmptyFields;

  @Inject
  @ConfigProperty(name = "jakarta.faces.FACELETS_SKIP_COMMENTS", defaultValue = "true")
  private Boolean skipComments;

  /**
   * Initialise la configuration JSF au démarrage de l'application.
   *
   * @param event Événement de construction de l'application.
   */
  public void initialize(@Observes @NotNull PostConstructApplicationEvent event) {
    try {
      log.info("Initialisation de la configuration JSF");

      configureFacesContext();
      configureViewHandler();
      configurePrimeFaces();

      applyPerformanceOptimizations();

      log.info("Configuration JSF initialisée avec succès - Mode: {}", projectStage);

    } catch (Exception e) {
      String message = "Erreur lors de l'initialisation de la configuration JSF";
      log.error(message, e);
      throw new ConfigurationException(message, e);
    }
  }

  /**
   * Nettoie les ressources JSF avant l'arrêt de l'application.
   *
   * @param event Événement de destruction de l'application.
   */
  public void cleanup(@Observes @NotNull PreDestroyApplicationEvent event) {
    log.info("Nettoyage des ressources JSF");
  }

  /**
   * Configure le contexte Faces avec les paramètres spécifiques.
   */
  private void configureFacesContext() {
    log.debug("Configuration du contexte Faces");

    setFacesParameter("jakarta.faces.PROJECT_STAGE", projectStage);
    setFacesParameter("jakarta.faces.STATE_SAVING_METHOD", stateSavingMethod);
    setFacesParameter("jakarta.faces.FACELETS_REFRESH_PERIOD", faceletsRefreshPeriod.toString());
    setFacesParameter("jakarta.faces.VALIDATE_EMPTY_FIELDS", validateEmptyFields.toString());
    setFacesParameter("jakarta.faces.FACELETS_SKIP_COMMENTS", skipComments.toString());
  }

  /**
   * Configure les paramètres spécifiques au gestionnaire de vue.
   */
  private void configureViewHandler() {
    log.debug("Configuration du gestionnaire de vues JSF");

    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (facesContext == null) {
      log.warn("Impossible de configurer le gestionnaire de vue : FacesContext est null.");
      return;
    }

    ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
    UIViewRoot root = facesContext.getViewRoot();

    if (root == null) {
      root = viewHandler.createView(facesContext, "/index.xhtml");
      facesContext.setViewRoot(root);
    }

    root.getAttributes().put("encoding", "UTF-8");
    root.getAttributes().put("contentType", "text/html");
    root.getAttributes().put("characterEncoding", "UTF-8");

    log.debug("Gestionnaire de vues configuré avec succès.");
  }

  /**
   * Configure les paramètres spécifiques à PrimeFaces.
   */
  private void configurePrimeFaces() {
    log.debug("Configuration de PrimeFaces");

    setFacesParameter("primefaces.THEME", primefacesTheme);
    setFacesParameter("primefaces.FONT_AWESOME", "true");
    setFacesParameter("primefaces.CLIENT_SIDE_VALIDATION", "true");
    setFacesParameter("primefaces.UPLOADER", "auto");

    configurePrimeFacesCache();
  }

  /**
   * Configure le cache PrimeFaces selon l'environnement.
   */
  private void configurePrimeFacesCache() {
    String cacheProvider = isDevelopmentMode() ? "memory" : "ehcache";
    setFacesParameter("primefaces.CACHE_PROVIDER", cacheProvider);
  }

  /**
   * Applique les optimisations de performance.
   */
  private void applyPerformanceOptimizations() {
    if (!isDevelopmentMode()) {
      setFacesParameter("jakarta.faces.FACELETS_REFRESH_PERIOD", "-1");
      setFacesParameter("jakarta.faces.COMPRESS_VIEWSTATE", "true");
      setFacesParameter("jakarta.faces.PARTIAL_STATE_SAVING", "true");
    }
  }

  /**
   * Définit un paramètre dans le contexte Faces.
   */
  private void setFacesParameter(String name, String value) {
    FacesContext.getCurrentInstance()
                .getExternalContext()
                .getApplicationMap()
                .put(name, value);

    log.debug("Paramètre défini : {} = {}", name, value);
  }

  /**
   * Vérifie si l'application est en mode développement.
   *
   * @return true si en mode développement, false sinon.
   */
  public boolean isDevelopmentMode() {
    return "Development".equals(projectStage);
  }
}
