import { BnumMessage } from '../../../mel_metapage/js/lib/classes/bnum_message.js';
import { ISO_FORMAT_REGEX } from '../../../mel_metapage/js/lib/constants/regexp.js';
import { MelHtml } from '../../../mel_metapage/js/lib/html/JsHtml/MelHtml.js';
import { MelTemplate } from '../../../mel_metapage/js/lib/html/JsHtml/MelTemplate.js';
import { MelObject } from '../../../mel_metapage/js/lib/mel_object.js';
import { Manager } from './manager.js';

export { PostComment, PostCommentView };

class PostComment {
  constructor(
    id,
    uid,
    post_id,
    user_email,
    user_name,
    content,
    created,
    likes,
    dislikes,
    parent,
    children_number,
    current_user_reacted,
  ) {
    this._init()._setup(
      id,
      uid,
      post_id,
      user_email,
      user_name,
      content,
      created,
      likes,
      dislikes,
      parent,
      children_number,
      current_user_reacted,
    );
    this.isProcessing = false;  // Ajout de l'état de traitement
  }

  /**
   * Initialise un objet avec des valeurs par défaut.
   *
   * Cette fonction réinitialise toutes les propriétés de l'objet, telles que
   * `uid`, `post_id`, `user_uid`, `user_name`, `content`, `created`, `like`,
   * `dislike`, `parent`, `children_number` et `current_user_reacted` à des chaînes de caractères vides.
   * Elle retourne l'objet lui-même après l'initialisation.
   *
   * @return {Object} L'objet initialisé avec des valeurs par défaut.
   */
  _init() {
    this.id = '';
    this.uid = '';
    this.post_id = '';
    this.user_email = '';
    this.user_name = '';
    this.content = '';
    this.created = '';
    this.likes = 0;
    this.dislikes = 0;
    this.parent = '';
    this.children_number = 0;
    this.current_user_reacted = '';

    return this;
  }

  /**
   * Configure les propriétés de l'objet avec les valeurs spécifiées.
   *
   * Cette fonction utilise `Object.defineProperties` pour définir les propriétés
   * `uid`, `post_id`, `user_uid`, `user_name`, `content`, `created`, `like`,
   * `dislike`, `parent`, `children_number` et `current_user_reacted` de l'objet. Chaque propriété a un getter qui
   * retourne la valeur initiale passée en paramètre, et un setter qui permet
   * de mettre à jour cette valeur.
   *
   * @param {string} id - L'identifiant de l'objet.
   * @param {string} uid - L'identifiant unique de l'objet.
   * @param {string} post_id - L'identifiant du post associé.
   * @param {string} user_email - L'email de l'utilisateur.
   * @param {string} user_name - Le nom de l'utilisateur.
   * @param {string} content - Le contenu du commentaire ou du post.
   * @param {string} created - La date de création.
   * @param {string} likes - Le nombre de likes.
   * @param {string} dislikes - Le nombre de dislikes.
   * @param {string} parent - L'Id du commentaire parent s'il existe'.
   * @param {integer} children_number - Le nombre de réponse au commentaire parent
   * @param {string} current_user_reacted - reaction de l'utilisateur courant au commentaire
   */
  _setup(
    id,
    uid,
    post_id,
    user_email,
    user_name,
    content,
    created,
    likes,
    dislikes,
    parent,
    children_number,
    current_user_reacted,
  ) {
    this.id = id;
    this.uid = uid;
    this.post_id = post_id;
    this.user_email = user_email;
    this.user_name = user_name;
    this.content = content;
    this.created = created;
    this.likes = likes !== undefined ? likes : 0; // Utilisation de 0 par défaut
    this.dislikes = dislikes !== undefined ? dislikes : 0; // Utilisation de 0 par défaut
    this.parent = parent || ''; // Valeur par défaut à une chaîne vide
    this.children_number = children_number !== undefined ? children_number : 0; // Valeur par défaut
    this.current_user_reacted = current_user_reacted || ''; // Valeur par défaut à une chaîne vide
  }

  /**
   * Enregistre ou met à jour une réaction (like ou dislike) sur un commentaire, et met à jour l'interface utilisateur.
   *
   * Cette fonction gère l'ajout ou la modification d'une réaction de l'utilisateur sur un commentaire.
   * Si une réaction (like ou dislike) est déjà présente, elle peut être annulée ou remplacée par une nouvelle réaction.
   * L'interface utilisateur est automatiquement mise à jour pour refléter le changement.
   *
   * Comportement :
   * - Envoie une requête au serveur pour enregistrer la réaction de l'utilisateur.
   * - Si l'utilisateur annule une réaction, la mise à jour visuelle correspondante est effectuée dans l'interface.
   * - Si l'utilisateur change de réaction (like vers dislike ou vice-versa), l'interface met à jour les compteurs.
   * - Les messages de confirmation ou d'erreur sont affichés en fonction de la réponse du serveur.
   *
   * @param {string} type - Le type de réaction, soit 'like' soit 'dislike'.
   * @param {string} uid - L'identifiant unique du commentaire sur lequel la réaction est appliquée.
   * @returns {Promise<Object>} Une promesse contenant la réponse du serveur, y compris le statut et le message.
   * @throws {Error} En cas d'échec lors de l'envoi de la requête ou de la mise à jour de la réaction.
   */
  async saveLikeOrDislike(type, uid) {
    try {
      // Vérification si une requête est déjà en cours
      if (this.isProcessing) {
        return; // Empêche les actions multiples simultanées
      }
  
      this.isProcessing = true; // Indique qu'une action est en cours
  
      // Fonction pour envoyer la requête
      const sendRequest = async (reactionType) =>
        await MelObject.Empty().http_internal_post({
          task: 'forum',
          action: 'like_comment',
          params: {
            _comment_id: this.id,
            _comment_uid: this.uid,
            _type: reactionType,
          },
        });
  
      // Désactiver les boutons pour éviter les clics simultanés
      let likeActionElement = $('[data-like-uid="' + uid + '"]');
      let dislikeActionElement = $('[data-dislike-uid="' + uid + '"]');
      likeActionElement.prop('disabled', true);
      dislikeActionElement.prop('disabled', true);
  
      // Envoie la requête avec le type de réaction (like ou dislike)
      let response = await sendRequest(type);
  
      // Réactiver les boutons après la réponse
      likeActionElement.prop('disabled', false);
      dislikeActionElement.prop('disabled', false);
  
      // Vérifier si la réponse contient un message
      if (response && response.message) {
        let likeCounterElement = $('[data-like-uid="' + uid + '"]').siblings('span.ml-2');
        let dislikeCounterElement = $('[data-dislike-uid="' + uid + '"]').siblings('span.ml-2');
  
        // Gestion de l'annulation d'une réaction
        if (response.message.includes('annulé')) {
          if (type === 'like') {
            likeCounterElement.text(Math.max(0, parseInt(likeCounterElement.text()) - 1));
            likeActionElement.parent().removeClass('active');
            this.current_user_reacted = ''; // Réinitialiser la réaction
          } else if (type === 'dislike') {
            // Réinitialiser l'état de `dislike`
            dislikeCounterElement.text(Math.max(0, parseInt(dislikeCounterElement.text()) - 1));
            dislikeActionElement.parent().removeClass('active');
            this.current_user_reacted = ''; // Réinitialiser la réaction
          }
        } else {
          // Ajout ou modification de la réaction
          if (type === 'like') {
            // Si un dislike est activé, on le retire
            if (dislikeActionElement.parent().hasClass('active')) {
              dislikeCounterElement.text(Math.max(0, parseInt(dislikeCounterElement.text()) - 1));
              dislikeActionElement.parent().removeClass('active');
            }
  
            // Si un like n'est pas déjà activé, on l'ajoute
            if (!likeActionElement.parent().hasClass('active')) {
              likeCounterElement.text(parseInt(likeCounterElement.text()) + 1);
              likeActionElement.parent().addClass('active');
            }
  
            this.current_user_reacted = 'like';
          } else if (type === 'dislike') {
            // Si un like est activé, on le retire
            if (likeActionElement.parent().hasClass('active')) {
              likeCounterElement.text(Math.max(0, parseInt(likeCounterElement.text()) - 1));
              likeActionElement.parent().removeClass('active');
            }
  
            // Si un dislike n'est pas déjà activé, on l'ajoute
            if (!dislikeActionElement.parent().hasClass('active')) {
              dislikeCounterElement.text(parseInt(dislikeCounterElement.text()) + 1);
              dislikeActionElement.parent().addClass('active');
            }
  
            this.current_user_reacted = 'dislike';
          }
        }
  
      } else {
        // Pas de message à afficher, aucune action supplémentaire ici
      }
  
    } catch (error) {
      // Gestion des erreurs de requêtes
      rcmail.display_message(
        rcmail.gettext('mel_forum.reaction_save_error'),
        'error',
      );
      console.error(
        rcmail.gettext('mel_forum.like_dislike_save_failure'),
        error,
      );
    } finally {
      // Réinitialisation de l'état de traitement après un délai
      setTimeout(() => {
        this.isProcessing = false;
      }, 1000); // Augmentation du délai pour limiter les clics rapides
    }
  }
  

  /**
   * Bascule l'affichage des réponses d'un commentaire et met à jour l'icône de basculement.
   *
   * Cette fonction gère l'affichage des réponses d'un commentaire en les affichant ou en les masquant
   * selon leur état actuel. Elle met à jour l'icône associée pour indiquer visuellement si les réponses
   * sont visibles ou masquées. Si les réponses ne sont pas encore chargées, elles sont récupérées et
   * affichées dynamiquement.
   *
   * Comportement :
   * - Si les réponses sont masquées, elles sont chargées puis affichées, et l'icône est changée pour "arrow_drop_up".
   * - Si les réponses sont visibles, elles sont masquées et l'icône est changée pour "arrow_drop_down".
   * - Le title de la div de basculement est mis à jour pour indiquer l'action associée (voir/masquer les réponses).
   * - Les réponses ne sont chargées qu'une seule fois pour éviter les doublons.
   *
   * @param {string} id - L'identifiant unique du commentaire pour lequel les réponses doivent être basculées.
   * @returns {Promise<void>} Une promesse résolue une fois que l'opération de basculement est effectuée.
   * @throws {Error} En cas d'échec lors du chargement ou de l'affichage des réponses.
   */
  async toggleResponses(id) {
    try {
      let responseContainer = $('#responses-' + id);
      let toggleIcon = $('#toggle-icon-' + id);
      let responseDiv = $('#toggle-response-container-' + id); // Utilisation de l'ID unique pour la div complète
      this.children_number = $('#comment-id-' + this.uid).data(
        'number-children',
      );
      let numberOfResponses = this.children_number; // Nombre de réponses

      // Fonction pour mettre à jour le title au survol de la div
      const updateTitle = () => {
        let titleText = '';
        if (numberOfResponses === 1) {
          titleText = responseContainer.hasClass('hidden')
            ? rcmail.gettext('mel_forum.see_response_singular')
            : rcmail.gettext('mel_forum.hide_reply');
        } else {
          titleText = responseContainer.hasClass('hidden')
            ? `${rcmail.gettext('mel_forum.see_the')} ${numberOfResponses} ${rcmail.gettext('mel_forum.response_plural')}`
            : `${rcmail.gettext('mel_forum.hide_items')} ${numberOfResponses} ${rcmail.gettext('mel_forum.response_plural')}`;
        }
        responseDiv.attr('title', titleText);
      };

      // Ajouter un gestionnaire d'événements au survol de la div
      responseDiv.on('mouseenter', updateTitle);

      // Si le conteneur est caché, on veut l'afficher
      if (responseContainer.hasClass('hidden')) {
        BnumMessage.SetBusyLoading();

        // Charger les réponses seulement si elles ne sont pas déjà présentes
        if (!responseContainer.hasClass('loaded')) {
          responseContainer.empty(); // On vide le conteneur avant de charger pour éviter les doublons
          await Manager.displayComments(null, id); // Charger les réponses une seule fois
          responseContainer.addClass('loaded'); // Marquer les réponses comme déjà chargées
        }

        BnumMessage.StopBusyLoading();

        // Afficher les réponses
        responseContainer.removeClass('hidden');
        toggleIcon.attr('data-icon', 'arrow_drop_up'); // Changer l'icône en 'arrow_drop_up'
      } else {
        // Cacher les réponses
        responseContainer.addClass('hidden');
        toggleIcon.attr('data-icon', 'arrow_drop_down'); // Changer l'icône en 'arrow_drop_down'
      }
    } catch (error) {
      console.error(rcmail.gettext('mel_forum.toggle_replies_error'), error);
    }
  }

  /**
   * Bascule l'affichage du formulaire de réponse et gère l'état des autres formulaires de réponse.
   *
   * Cette fonction masque tous les autres formulaires de réponse et affiche ou masque celui spécifié par `uid`.
   * Lorsqu'un formulaire de réponse est affiché, elle réinitialise le contenu du textarea et met le focus dessus.
   * L'identifiant du commentaire parent est également stocké pour être utilisé lors de l'envoi de la réponse.
   *
   * @param {string|number} uid - L'identifiant unique utilisé pour cibler le formulaire de réponse à afficher/masquer.
   * @param {string|number} [parentId] - L'ID du commentaire parent auquel la réponse sera associée.
   *                                      Si non fourni, utilise l'ID du commentaire actuel.
   *
   * @async
   * @returns {Promise<void>} - Aucune valeur de retour spécifique.
   */
  async toggleReplyForm(uid, parentId) {
    let form = $('#reply-form-' + uid);
    let isVisible = !form.hasClass('hidden');

    // Masquer tous les autres formulaires de réponse
    $('#reply-form').not(form).addClass('hidden');

    // Afficher ou masquer le formulaire actuel
    form.toggleClass('hidden');

    // Stocker l'ID du parent pour l'utiliser lors de l'envoi de la réponse
    this.parent = parentId || this.id; // Enregistre l'ID du commentaire parent dans `this.parent`

    // Réinitialiser le textarea lorsque le formulaire est visible
    if (!isVisible) {
      form.find('textarea').val('').height('auto');
      form.find('.btn').show(); // Assurez-vous que les boutons sont visibles
    }

    // Focus sur le textarea lorsque le formulaire est visible
    if (!form.hasClass('hidden')) {
      form.find('textarea').focus();
    }
  }

  /**
   * Enregistre une réponse à un commentaire et met à jour l'interface utilisateur en conséquence.
   *
   * Cette fonction récupère le contenu de la réponse à partir d'une zone de texte, vérifie qu'il n'est pas vide,
   * puis envoie une requête pour enregistrer la réponse. En cas de succès :
   * - Affiche un message de confirmation.
   * - Vide la zone de texte et cache le formulaire de réponse.
   * - Affiche la nouvelle réponse dans l'interface utilisateur et met à jour le nombre de réponses.
   *
   * En cas d'échec :
   * - Affiche un message d'erreur et consigne l'erreur dans la console.
   *
   * @async
   * @function saveReply
   * @returns {Promise<void>} - Une promesse qui se résout une fois la réponse enregistrée et l'interface mise à jour.
   * @throws {Error} Si une erreur survient lors de l'envoi de la requête ou de l'enregistrement de la réponse.
   */
  async saveReply() {
    const $textarea = $('#new-response-textarea-' + this.uid);
    const replyContent = $textarea.val(); // Récupérer le contenu du commentaire
    const submitButton = $('#submit-reply'); // Sélectionner le bouton de validation

    this.children_number = $('#comment-id-' + this.uid).data('number-children');

    // Récupération initiale de post_id
    this.post_id = rcmail.env.post_id;

    if (replyContent && replyContent.trim() !== '') {
        // Vérifier si le commentaire n'est pas vide

        submitButton.prop('disabled', true); // Désactiver le bouton de validation pour éviter les clics multiples

        BnumMessage.SetBusyLoading();

        try {
            const response = await MelObject.Empty().http_internal_post({
                task: 'forum',
                action: 'create_comment',
                params: {
                    _post_id: this.post_id, // L'ID du post
                    _content: replyContent, // Le contenu de la réponse
                    _parent: this.parent, // ID du commentaire parent
                },
            });

            if (response.status === 'success') {
                rcmail.display_message(response.message, 'confirmation');

                // Récupérer l'ID du commentaire créé
                const newCommentId = response.comment.id; // Assurez-vous que l'ID est inclus dans `response.comment`

                console.log('Nouveau commentaire ID :', newCommentId);

                // Vider le textarea
                $textarea.val('');

                // Fermer le formulaire en ajoutant la classe 'hidden'
                $('#reply-form-' + this.uid).addClass('hidden');

                const parent_comment_id = this.parent; // ID du commentaire parent
                let $responseContainer = $(
                    `#toggle-response-container-${parent_comment_id}`
                );

                // Vérifier si le conteneur existe
                if (!$responseContainer.length) {
                    // Si le conteneur n'existe pas, le créer avec MelHtml
                    this.children_number = 0;
                    const reponseText = 'réponse';
                    const newContainerHtml = MelHtml.start
                        .div({
                            id: 'toggle-response-container-' + parent_comment_id,
                            class: 'forum-comment-response',
                            'data-comment-id': parent_comment_id,
                            tabindex: '0',
                            role: 'button',
                            title:
                                this.children_number === 1
                                    ? rcmail.gettext('mel_forum.see_response_singular')
                                    : `${rcmail.gettext('mel_forum.see_the')} ${this.children_number} ${rcmail.gettext('mel_forum.response_plural')}`,
                        })
                        .span({
                            id: 'toggle-icon-' + parent_comment_id,
                            class: 'icon',
                            'data-icon': 'arrow_drop_down',
                        })
                        .end('span')
                        .span({ class: 'ml-2' })
                        .text(this.children_number + ' ' + reponseText)
                        .end('span')
                        .end('div')
                        .div({
                            id: 'responses-' + parent_comment_id,
                            class: 'responses ml-4 hidden',
                        })
                        .end('div')
                        .generate_html(true);

                    // Localiser la section où insérer le conteneur
                    const $responseSection = $(`#comment-id-${this.uid}`).find(
                        '.forum-comment-responses'
                    );
                    $responseSection.html(newContainerHtml);

                    // Re-sélectionner le conteneur après sa création
                    $responseContainer = $(
                        `#toggle-response-container-${parent_comment_id}`
                    );
                }

                // Attacher directement l'événement de clic à cet élément
                $responseContainer.on('click', () => {
                  this.toggleResponses(parent_comment_id); // Appel de la méthode toggleResponses
                });

                // Insérer la nouvelle réponse dans le conteneur
                await Manager.displaySingleComment(response.comment);

                // Mettre à jour le nombre de réponses dans l'interface
                const currentChildrenNumber = parseInt(this.children_number) + 1; // Incrémenter le nombre de réponses
                this.children_number = currentChildrenNumber; // Mettre à jour localement le nombre de réponses
                $('#comment-id-' + this.uid).data(
                    'number-children',
                    this.children_number
                );

                // Mettre à jour le texte du nombre de réponses
                const reponseText =
                    currentChildrenNumber === 1
                        ? rcmail.gettext('mel_forum.response_singular')
                        : rcmail.gettext('mel_forum.response_plural');
                $responseContainer
                    .find('span.ml-2')
                    .text(currentChildrenNumber + ' ' + reponseText);

                // Mettre à jour l'attribut `title`
                $responseContainer.attr(
                    'title',
                    currentChildrenNumber === 1
                        ? rcmail.gettext('mel_forum.see_response_singular')
                        : `${rcmail.gettext('mel_forum.see_the')} ${currentChildrenNumber} ${rcmail.gettext('mel_forum.response_plural')}`
                );
            } else {
                rcmail.display_message(response.message, 'error');
            }
        } catch (error) {
            rcmail.display_message(
                rcmail.gettext('mel_forum.reply_save_error'),
                'error'
            );
            console.error(rcmail.gettext('mel_forum.reply_save_failure'), error);
        } finally {
            BnumMessage.StopBusyLoading();

            // Réactiver le bouton de validation une fois la requête terminée
            submitButton.prop('disabled', false);
        }
    } else {
        rcmail.display_message(
            rcmail.gettext('mel_forum.comment_content_empty'),
            'error'
        );
    }
}


  /**
   * Bascule l'affichage d'un menu contextuel et applique des actions spécifiques lorsque le menu devient visible.
   * Cette fonction permet d'afficher ou de masquer un conteneur de menu contextuel identifié par `uid`.
   * Lorsque le menu est visible, elle ajoute un écouteur d'événements pour détecter les clics extérieurs et fermer
   * le menu en conséquence. Elle gère également les événements liés aux options du menu.
   *
   * @param {string|number} uid - L'identifiant unique utilisé pour cibler le conteneur de menu.
   *
   * - Le conteneur ciblé est supposé avoir un identifiant au format `#context-menu-{uid}`.
   * - Si le conteneur devient visible, les clics en dehors du menu le referment automatiquement.
   * - Les événements sur les boutons du menu sont gérés pour fermer le menu après une sélection.
   */
  toggleMenu(uid) {
    let selectContainer = $('#context-menu-' + uid);
    let triggerButton = $('#trigger-' + uid); // Bouton more_horiz

    // Vérifier si le conteneur du menu existe
    if (selectContainer.length) {
      // Basculer l'affichage du conteneur
      selectContainer.toggleClass('hidden');

      // Si le menu est visible, ajouter un écouteur pour détecter les clics extérieurs
      if (!selectContainer.hasClass('hidden')) {
        // Ajouter un écouteur de clic sur tout le document après un léger délai
        setTimeout(() => {
          $(document).on('click.menuOutside', function (event) {
            // Vérifier si le clic est en dehors du menu et du bouton trigger
            if (
              !$(event.target).closest(selectContainer).length &&
              !$(event.target).closest(triggerButton).length
            ) {
              selectContainer.addClass('hidden'); // Masquer le menu
              $(document).off('click.menuOutside'); // Retirer l'écouteur après fermeture
            }
          });

          // Ajouter un écouteur d'événements pour chaque bouton du menu
          selectContainer
            .find('.comment-options-button')
            .on('click', function () {
              selectContainer.addClass('hidden'); // Fermer le menu
              $(document).off('click.menuOutside'); // Retirer l'écouteur après fermeture
            });
        }, 0); // Délai de 0 pour que l'événement de clic sur le bouton soit géré en premier

        // Empêcher la propagation du clic sur le bouton trigger pour éviter la fermeture immédiate
        triggerButton.off('click').on('click', function (event) {
          event.stopPropagation(); // Empêche la propagation du clic vers l'écouteur du document
        });
      } else {
        // Si le menu est caché, retirer l'écouteur du document
        $(document).off('click.menuOutside');
      }
    }
  }

  /**
   * Basculer l'affichage entre le texte du commentaire et le champ de texte de modification.
   *
   * Cette fonction permet d'afficher ou de masquer la section de modification du commentaire
   * en fonction de l'état actuel de l'affichage.
   *
   * @function toggleModifyComment
   * @param {string} uid - L'identifiant unique du commentaire à modifier.
   * @returns {void}
   */
  toggleModifyComment(uid) {
    let commentTextDiv = $('#comment-text-' + uid);
    let editTextDiv = $('#edit-comment-' + uid);

    // Basculer entre le content et le textarea de modification
    commentTextDiv.toggleClass('hidden');
    editTextDiv.toggleClass('hidden');
  }

  /**
   * Annule la modification du commentaire en rétablissant l'affichage initial.
   *
   * Cette fonction appelle `toggleModifyComment` pour basculer l'affichage
   * entre la section de texte et la section de modification.
   *
   * @function cancelModifyComment
   * @param {string} uid - L'identifiant unique du commentaire dont la modification est annulée.
   * @returns {void}
   */
  cancelModifyComment(uid) {
    this.toggleModifyComment(uid);
  }

  /**
   * Enregistre les modifications d'un commentaire et met à jour l'interface utilisateur en conséquence.
   *
   * @async
   * @returns {Promise<void>} - Une promesse qui se résout lorsque la modification est enregistrée et l'interface mise à jour.
   *
   * Cette fonction récupère le contenu modifié du commentaire à partir d'une zone de texte spécifique, vérifie que le contenu n'est pas vide,
   * puis envoie une requête pour enregistrer la modification. Si l'enregistrement est réussi :
   * - Affiche un message de confirmation.
   * - Cache le formulaire de modification.
   * - Met à jour le texte du commentaire dans l'interface utilisateur.
   *
   * En cas d'échec ou d'erreur lors de l'enregistrement :
   * - Affiche un message d'erreur.
   * - Enregistre l'erreur dans la console.
   */
  async modifyComment(uid) {
    const $textarea = $('#edit-comment-textarea-' + uid);
    const updatedContent = $textarea.val(); // Récupère le nouveau contenu du commentaire
    if (updatedContent && updatedContent.trim() !== '') {
      BnumMessage.SetBusyLoading();

      try {
        let response = await MelObject.Empty().http_internal_post({
          task: 'forum',
          action: 'update_comment',
          params: {
            _uid: uid, // L'ID du commentaire
            _content: updatedContent, // Le nouveau contenu
          },
        });

        if (response.status === 'success') {
          rcmail.display_message(response.message, 'confirmation');

          // Fermer le formulaire de modification en ajoutant la classe 'hidden'
          $('#edit-comment-' + uid).addClass('hidden');

          // Mettre à jour l'affichage du commentaire avec le nouveau contenu
          $('#comment-text-' + uid).replaceWith(
            '<div class="forum-comment-text" id="comment-text-' +
              uid +
              '"><p>' +
              updatedContent +
              '</p></div>',
          );
        } else if (response.status === 'error') {
          rcmail.display_message(response.message, 'error');
        }
      } catch (error) {
        rcmail.display_message(
          'Une erreur est survenue lors de la mise à jour du commentaire.',
          'error',
        );
        console.error('Erreur lors de la mise à jour du commentaire:', error);
      } finally {
        BnumMessage.StopBusyLoading();
      }
    } else {
      rcmail.display_message(
        'Le contenu du commentaire ne peut pas être vide.',
        'error',
      );
    }
  }

  /**
   * Supprime un commentaire spécifique après confirmation de l'utilisateur.
   *
   * Cette fonction envoie une requête à l'API de suppression du commentaire,
   * puis met à jour l'affichage en retirant le commentaire supprimé.
   *
   * @async
   * @function deleteComment
   * @param {string} uid - L'identifiant unique du commentaire à supprimer.
   * @returns {void}
   * @throws {Error} En cas d'échec de la suppression ou d'une erreur réseau.
   */
  async deleteComment(uid) {
    // Demander confirmation à l'utilisateur avant de supprimer
    const confirmation = confirm(
      'Êtes-vous sûr de vouloir supprimer ce commentaire ?',
    );

    if (!confirmation) return;

    BnumMessage.SetBusyLoading();

    try {
      const response = await MelObject.Empty().http_internal_post({
        task: 'forum',
        action: 'delete_comment',
        params: {
          _uid: uid, // L'ID du commentaire à supprimer
          _parent: this.parent, // ID du commentaire parent
        },
      });

      const parsedResponse = JSON.parse(response);

      if (parsedResponse.status === 'success') {
        rcmail.display_message(parsedResponse.message, 'confirmation');

        // Supprimer le commentaire de l'affichage
        const commentElement = $('#comment-id-' + uid);

        if (commentElement.length > 0) {
          commentElement.remove(); // Supprimer le commentaire du DOM
        }

        // Identifier le conteneur parent des réponses à partir de l'élément supprimé
        const parent_comment_id = this.parent; // ID du commentaire parent
        const $responseContainer = $(
          `#toggle-response-container-${parent_comment_id}`,
        );
        const $parentContainer = $responseContainer.parent().parent().parent();

        // Récupérer le nombre actuel de réponses du commentaire parent
        let currentChildrenNumber = $parentContainer.data('number-children');
        currentChildrenNumber = parseInt(currentChildrenNumber) - 1;
        $parentContainer.data('number-children', currentChildrenNumber);
        // const currentChildrenNumber = parseInt(this.children_number) - 1; // Décrémenter le nombre de réponses
        // this.children_number = currentChildrenNumber; // Mettre à jour localement le nombre de réponses

        // Mettre à jour l'affichage en fonction du nouveau nombre de réponses
        if (currentChildrenNumber > 0) {
          const reponseText =
            currentChildrenNumber === 1
              ? rcmail.gettext('mel_forum.response_singular')
              : rcmail.gettext('mel_forum.response_plural');
          $responseContainer
            .find('span.ml-2')
            .text(currentChildrenNumber + ' ' + reponseText);

          // Mettre à jour l'attribut `title`
          $responseContainer.attr(
            'title',
            currentChildrenNumber === 1
              ? rcmail.gettext('mel_forum.see_response_singular')
              : `${rcmail.gettext('mel_forum.see_the')} ${currentChildrenNumber} ${rcmail.gettext('mel_forum.response_plural')}`,
          );
          // on s'assure que le conteneur des réponses est visible
          $responseContainer.removeClass('hidden');
          $(`#responses-${parent_comment_id}`).removeClass('hidden'); // Afficher le conteneur des réponses
        } else {
          // S'il n'y a plus de réponses, masquer la section des réponses et l'icône
          $responseContainer.addClass('hidden'); // Masquer la section "Voir X réponses"
          $(`#responses-${parent_comment_id}`).addClass('hidden'); // Masquer le conteneur des réponses
        }
      } else {
        rcmail.display_message(parsedResponse.message, 'error');
      }
    } catch (error) {
      rcmail.display_message(
        rcmail.gettext('mel_forum.comment_delete_error'),
        'error',
      );
      console.error(rcmail.gettext('mel_forum.comment_delete_failure'), error);
    } finally {
      BnumMessage.StopBusyLoading();
    }
  }

  /**
   * Génère le code HTML pour afficher un commentaire avec ses réactions et options associées.
   *
   * Cette fonction crée dynamiquement le HTML pour un commentaire, en intégrant des informations
   * comme l'auteur, le contenu, la date de création, ainsi que les boutons d'interaction (like, dislike, répondre).
   * Elle adapte le HTML en fonction des données spécifiques du commentaire, notamment le nombre de likes, dislikes,
   * et réponses.
   *
   * - Les classes CSS sont ajustées en fonction de la réaction de l'utilisateur (like/dislike).
   * - Le texte des réponses est singularisé ou pluralisé selon leur nombre.
   * - Un élément interactif pour afficher ou masquer les réponses est ajouté si le commentaire en contient.
   *
   * @returns {Object} - Un objet HTML généré, prêt à être inséré dans le DOM.
   */
  generateHtmlFromTemplate() {
    let likeClass =
      this.current_user_reacted === 'like'
        ? 'reaction-item active mr-3'
        : 'reaction-item mr-3';
    let dislikeClass =
      this.current_user_reacted === 'dislike'
        ? 'reaction-item active mr-3'
        : 'reaction-item mr-3';

    // Détermination du pluriel ou du singulier pour "réponse(s)"
    let reponseText =
      this.children_number > 1
        ? rcmail.gettext('mel_forum.response_plural')
        : rcmail.gettext('mel_forum.response_singular');

    // Fonction pour parser une date en français
    function parseFrenchDate(dateString) {
      // Vérifiez si la date est au format ISO (ex: "2024-10-23 12:55:47")
      const isoFormatRegex = ISO_FORMAT_REGEX;

      if (isoFormatRegex.test(dateString)) {
        // Convertir directement la chaîne ISO en objet Date
        return new Date(dateString.replace(' ', 'T')); // Remplacer l'espace par 'T' pour le format ISO
      }
      const moisFrancais = {
        janvier: 0,
        février: 1,
        mars: 2,
        avril: 3,
        mai: 4,
        juin: 5,
        juillet: 6,
        août: 7,
        septembre: 8,
        octobre: 9,
        novembre: 10,
        décembre: 11,
      };

      // Séparer la date et l'heure si une heure est incluse
      const [datePart, timePart] = dateString.split(' à ');

      // Séparer la partie date en jour, mois et année
      const dateParts = datePart.trim().split(' ');

      if (dateParts.length !== 3) {
        console.error(
          rcmail.gettext('mel_forum.invalid_date_format'),
          dateString,
        );
        return null;
      }

      const jour = parseInt(dateParts[0], 10);
      const mois = dateParts[1].toLowerCase();
      const annee = parseInt(dateParts[2], 10);

      const moisIndex = moisFrancais[mois];

      if (moisIndex === undefined) {
        console.error(rcmail.gettext('mel_forum.invalid_date_month'), mois);
        return null;
      }

      // Initialiser l'heure et les minutes à 0
      let heures = 0,
        minutes = 0;

      // Si l'heure est présente, extraire l'heure et les minutes
      if (timePart) {
        const [heuresStr, minutesStr] = timePart.split(':');
        heures = parseInt(heuresStr, 10) || 0;
        minutes = parseInt(minutesStr, 10) || 0;
      }

      // Créer l'objet Date avec la date et l'heure
      return new Date(annee, moisIndex, jour, heures, minutes);
    }

    // Fonction pour parser une date en français
    function formatCommentDate(createdDate) {
      const commentDate = parseFrenchDate(createdDate);

      // Vérifier si la date est valide
      if (!commentDate || isNaN(commentDate.getTime())) {
        console.error(rcmail.gettext('mel_forum.invalid_date'), createdDate);
        return rcmail.gettext('mel_forum.invalid_date_simple');
      }

      const currentDate = new Date();

      // Ne comparer que les jours, mois, années (ignorer heures, minutes)
      const commentDateOnly = new Date(
        commentDate.getFullYear(),
        commentDate.getMonth(),
        commentDate.getDate(),
      );
      const currentDateOnly = new Date(
        currentDate.getFullYear(),
        currentDate.getMonth(),
        currentDate.getDate(),
      );

      // Calculer la différence en jours uniquement
      const diffTime = currentDateOnly.getTime() - commentDateOnly.getTime();
      const diffDays = Math.floor(diffTime / (1000 * 3600 * 24));

      const hours = commentDate.getHours().toString().padStart(2, '0');
      const minutes = commentDate.getMinutes().toString().padStart(2, '0');
      const timeString = `${hours}h${minutes}`;

      if (diffDays === 0) {
        return `${rcmail.gettext('mel_forum.today_at')} ${timeString}`;
      } else if (diffDays === 1) {
        return `${rcmail.gettext('mel_forum.yesterday_at')} ${timeString}`;
      } else {
        const options = { year: 'numeric', month: 'long', day: 'numeric' };
        const dateString = commentDate.toLocaleDateString('fr-FR', options);
        return `${dateString}`;
      }
    }

    // Préparez les données à insérer dans le template
    const data = {
      UID: this.uid,
      USER_EMAIL: this.user_email,
      USER_NAME: this.user_name,
      COMMENT_DATE: formatCommentDate(this.created),
      COMMENT_CONTENT: this.content,
      COMMENT_ID: this.id,
      LIKE_CLASS: likeClass,
      DISLIKE_CLASS: dislikeClass,
      LIKES: this.likes.toString(),
      DISLIKES: this.dislikes.toString(),
      NUMBER_CHILDREN: this.children_number,
      RESPONSE_SECTION:
        this.children_number > 0
          ? MelHtml.start
              .div({
                id: 'toggle-response-container-' + this.id,
                class: 'forum-comment-response',
                'data-comment-id': this.id,
                tabindex: '0',
                role: 'button',
                title:
                  this.children_number === 1
                    ? rcmail.gettext('mel_forum.see_response_singular')
                    : `${rcmail.gettext('mel_forum.see_the')} ${this.children_number} ${rcmail.gettext('mel_forum.response_plural')}`,
              })
              .span({
                id: 'toggle-icon-' + this.id,
                class: 'icon',
                'data-icon': 'arrow_drop_down',
              })
              .end('span')
              .span({ class: 'ml-2' })
              .text(this.children_number + ' ' + reponseText)
              .end('span')
              .end('div')
              .div({
                id: 'responses-' + this.id,
                class: 'responses ml-4 hidden',
              })
              .end('div')
              .generate_html(true)
          : '',
    };

    // Utilisation de MelTemplate
    let template = new MelTemplate()
      .setTemplateSelector('#comment-template')
      .setData(data)
      .addEvent(
        '#cancel-modify-comment',
        'click',
        this.cancelModifyComment.bind(this, this.uid),
      )
      .addEvent(
        '#submit-modify-comment',
        'click',
        this.modifyComment.bind(this, this.uid),
      )
      .addEvent(
        '.icon[data-icon="thumb_up"]',
        'click',
        this.saveLikeOrDislike.bind(this, 'like', this.uid),
      )
      .addEvent(
        '.icon[data-icon="thumb_down"]',
        'click',
        this.saveLikeOrDislike.bind(this, 'dislike', this.uid),
      )
      .addEvent(
        '.reaction-item.response',
        'click',
        this.toggleReplyForm.bind(this, this.uid, this.id),
      )
      .addEvent(
        '.icon[data-icon="more_horiz"]',
        'click',
        this.toggleMenu.bind(this, this.uid),
      )
      .addEvent(
        '.comment-options-button.edit-comment',
        'click',
        this.toggleModifyComment.bind(this, this.uid),
      )
      .addEvent(
        '.comment-options-button.delete-comment',
        'click',
        this.deleteComment.bind(this, this.uid),
      )
      .addEvent(
        '#cancel-reply',
        'click',
        this.toggleReplyForm.bind(this, this.uid, this.id),
      )
      .addEvent(
        '#submit-reply',
        'click',
        this.saveReply.bind(this, this.content),
      );

    // Ajouter l'événement pour '.forum-comment-response' seulement si elle existe
    if (this.children_number > 0) {
      template.addEvent(
        '.forum-comment-response',
        'click',
        this.toggleResponses.bind(this, this.id),
      );
    }

    // Retourner le rendu complet sans l'ajouter au DOM
    return template.render();
  }
}

class PostCommentView {
  constructor(
    post_uid,
    post_id,
    sort_order = 'date_asc',
    parent_comment_id = null,
  ) {
    this._init()._setup(post_uid, post_id, sort_order, parent_comment_id);
  }

  /**
   * Initialise l'objet avec des valeurs par défaut pour les propriétés `post_uid`, `post_id`,
   * `sort_order` et `parent_comment_id`.
   *
   * Cette fonction réinitialise les propriétés de l'objet avec des valeurs par défaut :
   * une chaîne vide pour `post_uid` et `post_id`, un tri par date ascendante pour `sort_order`,
   * et `null` pour `parent_comment_id`. Elle retourne l'objet lui-même après initialisation.
   *
   * @returns {Object} - L'objet initialisé avec les valeurs par défaut.
   */
  _init() {
    this.post_uid = '';
    this.post_id = '';
    this.sort_order = 'date_asc';
    this.parent_comment_id = null;

    return this;
  }

  /**
   * Configure les propriétés `post_uid`, `post_id`, `sort_order` et `parent_comment_id` de l'objet.
   *
   * Cette fonction initialise les propriétés de l'objet en définissant l'UID du post,
   * l'identifiant du post, l'ordre de tri des commentaires, ainsi que l'ID du commentaire parent
   * si fourni. Ces propriétés peuvent ensuite être utilisées dans d'autres méthodes.
   *
   * @param {string} post_uid - L'UID du post à configurer.
   * @param {string} post_id - L'identifiant du post à configurer.
   * @param {string} sort_order - L'ordre de tri à appliquer aux commentaires.
   * @param {string} [parent_comment_id] - L'ID du commentaire parent, si applicable.
   */
  _setup(post_uid, post_id, sort_order, parent_comment_id) {
    this.post_uid = post_uid;
    this.post_id = post_id;
    this.sort_order = sort_order;
    this.parent_comment_id = parent_comment_id;
  }

  /**
   * Récupère le commentaire associé à un post spécifique.
   *
   * Cette fonction envoie une requête asynchrone pour récupérer le commentaire
   * d'un post donné, en tenant compte de l'ordre de tri et éventuellement d'un
   * commentaire parent si fourni. Les données de réponse sont analysées et renvoyées.
   *
   * @async
   * @function getCommentByPost
   * @returns {Promise<Object>} - Les données du commentaire du post, après analyse de la réponse.
   */
  async getCommentByPost() {
    BnumMessage.SetBusyLoading();

    let return_data;

    // Préparer les données à envoyer
    let postData = {
      _post_uid: this.post_uid,
      _sort_order: this.sort_order, // Envoi du paramètre 'sort_order' au serveur
    };

    // Si un ID de commentaire parent est fourni, l'ajouter aux données
    if (this.parent_comment_id) {
      postData._comment_id = this.parent_comment_id; // Envoi de l'ID du commentaire parent si disponible
    }

    try {
      // Effectuer la requête avec les données préparées
      const datas = await MelObject.Empty().http_internal_post({
        task: 'forum',
        action: 'get_all_comments_bypost',
        params: postData, // Les données incluent l'ID du parent si fourni
      });

      return_data = JSON.parse(datas);
    } catch (error) {
      console.error(rcmail.gettext('mel_forum.comments_fetch_error'), error);
    } finally {
      BnumMessage.StopBusyLoading();
    }

    return return_data;
  }
}