package dev.lions.controllers;

import jakarta.enterprise.context.SessionScoped;
import jakarta.enterprise.event.Event;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotNull;

import java.io.Serial;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import dev.lions.events.NavigationEvent;
import dev.lions.services.ProjectService;

import java.io.Serializable;
import java.util.Map;
import java.util.HashMap;
import java.util.Optional;

/**
 * Contrôleur de navigation principal de l'application.
 * Gère les transitions entre les pages et la persistance des paramètres
 * de navigation de manière sécurisée et optimisée.
 */
@Slf4j
@Named("navigationController")
@SessionScoped
public class NavigationController implements Serializable {

  @Serial
  private static final long serialVersionUID = 1L;
  private static final String DEFAULT_OUTCOME = "/public/index.xhtml";

  @Inject
  ProjectService projectService;

  @Inject
  Event<NavigationEvent> navigationEvent;

  @Getter
  private String currentPage;

  private final Map<String, Object> navigationParams = new HashMap<>();

  /**
   * Redirige vers la page d'accueil avec rafraîchissement.
   *
   * @return Résultat de la navigation JSF
   */
  public String goToHome() {
    log.info("Navigation vers la page d'accueil");
    notifyNavigation("home");
    return DEFAULT_OUTCOME + "?faces-redirect=true";
  }

  /**
   * Redirige vers la page des services.
   *
   * @return Résultat de la navigation JSF
   */
  public String goToServices() {
    log.info("Navigation vers la page des services");
    notifyNavigation("services");
    return "/private/services.xhtml?faces-redirect=true";
  }

  /**
   * Redirige vers la page du portfolio.
   *
   * @return Résultat de la navigation JSF
   */
  public String goToPortfolio() {
    log.info("Navigation vers le portfolio");
    notifyNavigation("portfolio");
    return "/private/portfolio.xhtml?faces-redirect=true";
  }

  /**
   * Redirige vers la page de détails d'un projet spécifique.
   *
   * @param projectId Identifiant du projet
   * @return Résultat de la navigation JSF
   */
  public String goToProjectDetails(@NotNull String projectId) {
    log.info("Navigation vers les détails du projet: {}", projectId);

    if (!projectService.existsById(projectId)) {
      log.warn("Tentative d'accès à un projet inexistant: {}", projectId);
      addErrorMessage();
      return null;
    }

    notifyNavigation("project_details", Map.of("projectId", projectId));
    return "/private/project-details.xhtml?faces-redirect=true&id=" + projectId;
  }

  /**
   * Redirige vers la page de détails d'un service spécifique.
   *
   * @param serviceId Identifiant du service
   * @return Résultat de la navigation JSF
   */
  public String goToServiceDetails(@NotNull String serviceId) {
    log.info("Navigation vers les détails du service: {}", serviceId);
    notifyNavigation("service_details", Map.of("serviceId", serviceId));
    return "/private/service-details.xhtml?faces-redirect=true&id=" + serviceId;
  }

  /**
   * Redirige vers la page de contact.
   *
   * @return Résultat de la navigation JSF
   */
  public String goToContact() {
    log.info("Navigation vers la page de contact");
    notifyNavigation("contact");
    return "/private/contact.xhtml?faces-redirect=true";
  }

  /**
   * Sauvegarde un paramètre pour la navigation.
   *
   * @param key Clé du paramètre
   * @param value Valeur du paramètre
   */
  public void setNavigationParameter(@NotNull String key, Object value) {
    navigationParams.put(key, value);
    log.debug("Paramètre de navigation défini: {} = {}", key, value);
  }

  /**
   * Récupère un paramètre de navigation.
   *
   * @param key Clé du paramètre
   * @return Valeur du paramètre ou null si non trouvé
   */
  public Object getNavigationParameter(String key) {
    return navigationParams.get(key);
  }

  /**
   * Vérifie si la page actuelle correspond à un chemin donné.
   *
   * @param viewId Identifiant de la vue
   * @return true si la page courante correspond
   */
  public boolean isCurrentPage(String viewId) {
    String currentViewId = FacesContext.getCurrentInstance()
                                       .getViewRoot()
                                       .getViewId();
    return currentViewId.equals(viewId);
  }

  /**
   * Nettoie les paramètres de navigation.
   */
  public void clearNavigationParameters() {
    navigationParams.clear();
    log.debug("Paramètres de navigation nettoyés");
  }

  /**
   * Initialise un nouveau contexte de navigation.
   */
  public void initializeNavigation() {
    currentPage = Optional.ofNullable(FacesContext.getCurrentInstance())
                          .map(context -> context.getViewRoot().getViewId())
                          .orElse(DEFAULT_OUTCOME);

    log.info("Navigation initialisée sur la page: {}", currentPage);
  }

  /**
   * Notifie le système d'un événement de navigation.
   */
  private void notifyNavigation(String destination) {
    notifyNavigation(destination, new HashMap<>());
  }

  /**
   * Notifie le système d'un événement de navigation avec paramètres.
   */
  private void notifyNavigation(String destination, Map<String, Object> params) {
    NavigationEvent event = NavigationEvent.builder()
                                           .source(currentPage)
                                           .destination(destination)
                                           .parameters(params)
                                           .timestamp(System.currentTimeMillis())
                                           .build();

    navigationEvent.fire(event);
  }

  /**
   * Ajoute un message d'erreur dans le contexte Faces.
   */
  private void addErrorMessage() {
    FacesContext.getCurrentInstance()
                .addMessage(null,
                            new jakarta.faces.application.FacesMessage(
                                jakarta.faces.application.FacesMessage.SEVERITY_ERROR,
                                "Erreur", "Projet introuvable"
                            )
                );
  }
}