/**
 *
 * Ensemble des fonctions liées à la gestion des assiduités
 * Créé par : HARTMANN Matthias (Iziram)
 *
 */

/**
 * <== OUTILS ==>
 */

/**
 * Ajout d'une fonction `capitalize` sur tous les strings
 * "alice".capitalize() -> "Alice"
 */
Object.defineProperty(String.prototype, "capitalize", {
  value: function () {
    return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
  },
  enumerable: false,
});

/**
 * Fait une requête GET de façon asynchrone
 * @param {String} path adresse distante
 * @param {CallableFunction} success fonction à effectuer en cas de succès
 * @param {CallableFunction} errors fonction à effectuer en cas d'échec
 */
async function async_get(path, success, errors) {
  const response = fetch(path);
  response
    .then((response) => {
      if (response.ok) {
        response.json().then((data) => {
          success(data, "success");
        });
      } else {
        throw new Error("Network response was not ok.");
      }
    })
    .catch((error) => {
      console.error(error);
      if (errors) errors(error);
    });

  return response;
}
/**
 * Fait une requête POST de façon asynchrone
 * @param {String} path adresse distante
 * @param {object} data données à envoyer (objet js)
 * @param {CallableFunction} success fonction à effectuer en cas de succès
 * @param {CallableFunction} errors fonction à effectuer en cas d'échec
 */
async function async_post(path, data, success, errors) {
  console.log("async_post " + path);
  let response;
  try {
    response = await fetch(path, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });

    if (response.ok) {
      const responseData = await response.json();
      success(responseData);
    } else {
      throw new Error("Network response was not ok.");
    }
  } catch (error) {
    console.error(error);
    if (errors) errors(error);
  }

  return response;
}

/**
 * Récupère les étudiants en fonction des groupes sélectionnés
 * @param {Array} groupIds - Les identifiants des groupes pour lesquels récupérer les étudiants.
 * @returns {Promise<Object>} Un objet contenant les étudiants, indexés par leur identifiant.
 */
async function recupEtuds(groupIds) {
  const etuds = new Map();
  if (groupIds == null || groupIds.length == 0) return etuds;

  // Créer un tableau de promesses pour chaque requête GET asynchrone.
  let requests = groupIds.map((groupId) =>
    fetch(`../../api/group/${groupId}/etudiants`)
      .then((response) => {
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        return response.json();
      })
      .then((data) => {
        data.forEach((etud) => {
          etuds.set(etud.id, etud);
        });
      })
      .catch((error) =>
        console.error(
          "There has been a problem with your fetch operation:",
          error
        )
      )
  );

  // Attendre que toutes les promesses dans le tableau `requests` soient résolues.
  await Promise.all(requests);

  return etuds;
}
/**
 * Récupère l'assiduité des étudiants pour une date donnée
 * @param {Map} etuds
 * @param {Date} date
 */
async function recupAssiduites(etuds, date) {
  const etudIds = [...etuds.keys()].join(",");

  const date_debut = date.add(-1, "days").format("YYYY-MM-DDTHH:mm");
  const date_fin = date.add(2, "days").format("YYYY-MM-DDTHH:mm");

  url =
    `../../api/assiduites/group/query?date_debut=${date_debut}` +
    `&date_fin=${date_fin}&etudids=${etudIds}&with_justifs`;

  await fetch(url)
    .then((res) => {
      if (!res.ok) {
        throw new Error("Network response was not ok");
      }
      return res.json();
    })
    .then((data) => {
      Object.keys(data).forEach((etudid) => {
        const etud = etuds.get(Number(etudid));
        const assiduites = data[etudid];
        etud.assiduites = assiduites;
      });
    })
    .catch((error) =>
      console.error(
        "There has been a problem with your fetch operation:",
        error
      )
    );
}

/**
 * Génération ligne étudiante
 */
function creerLigneEtudiant(etud, index) {
  let currentAssiduite = {
    etat: "",
    type: "creation",
    assiduite_id: -1,
    date_debut: null,
    date_fin: null,
  };

  function recupConflitsAssiduites(assiduites) {
    const period = getPeriodAsDate();

    return assiduites.filter((assi) => {
      const interval = {
        deb: new Date(Date.removeUTC(assi.date_debut)),
        fin: new Date(Date.removeUTC(assi.date_fin)),
      };

      return (
        period.deb.isBefore(interval.fin) && period.fin.isAfter(interval.deb)
      );
    });
  }

  const conflits = readOnly ? [] : recupConflitsAssiduites(etud.assiduites);

  if (conflits.length > 0) {
    currentAssiduite = conflits[0];

    const conflitsPeriode = {
      deb: new Date(Date.removeUTC(currentAssiduite.date_debut)),
      fin: new Date(Date.removeUTC(currentAssiduite.date_fin)),
    };
    const period = getPeriodAsDate();
    currentAssiduite.type =
      period.deb.isSame(conflitsPeriode.deb) &&
      period.fin.isSame(conflitsPeriode.fin)
        ? "edition"
        : "conflit";
  }

  const ligneEtud = document.createElement("div");
  ligneEtud.classList.add("etud_row");
  if (Object.keys(etudsDefDem).includes(etud.id)) {
    ligneEtud.classList.add(etudsDefDem[etud.id] == "D" ? "dem" : "def");
  }
  ligneEtud.id = `etud_row_${etud.id}`;

  if (currentAssiduite.type === "conflit" && !readOnly)
    ligneEtud.classList.add("conflit");

  // div index avec l'index
  const indexDiv = document.createElement("div");
  indexDiv.classList.add("index");
  indexDiv.textContent = index;

  ligneEtud.appendChild(indexDiv);

  // div name_field

  const nameField = document.createElement("div");
  nameField.classList.add("name_field");

  if ($("#pdp").is(":checked")) {
    const pdp = document.createElement("img");
    pdp.src = `../../api/etudiant/etudid/${etud.id}/photo?size=small`;
    pdp.alt = `${etud.nom} ${etud.prenom}`;
    pdp.classList.add("pdp");
    nameField.appendChild(pdp);
  }

  const nameSet = document.createElement("a");
  nameSet.classList.add("name_set");
  nameSet.href = `bilan_etud?etudid=${etud.id}`;

  const nom = document.createElement("h4");
  nom.classList.add("nom");
  nom.textContent = etud.nom;

  const prenom = document.createElement("h5");
  prenom.classList.add("prenom");
  prenom.textContent = etud.prenom;

  nameSet.appendChild(nom);
  nameSet.appendChild(prenom);

  nameField.appendChild(nameSet);
  ligneEtud.appendChild(nameField);

  // div assiduites_bar

  const assiduitesBar = document.createElement("div");
  assiduitesBar.classList.add("assiduites_bar");

  const prevDateAssi = document.createElement("div");
  prevDateAssi.id = "prevDateAssi";

  function recupDerniereAssiduite(assiduites) {
    const period = {
      deb: $("#date").datepicker("getDate").add(-1, "days"),
      fin: $("#date").datepicker("getDate"),
    };

    const lastAssiduite = assiduites
      .filter((assi) => {
        const interval = {
          deb: new Date(Date.removeUTC(assi.date_debut)),
          fin: new Date(Date.removeUTC(assi.date_fin)),
        };

        return (
          period.deb.isBefore(interval.fin) && period.fin.isAfter(interval.deb)
        );
      })
      .sort((a, b) => {
        return (
          new Date(Date.removeUTC(b.date_debut)) -
          new Date(Date.removeUTC(a.date_debut))
        );
      })
      .pop();

    return lastAssiduite ?? null;
  }

  const lastAssiduite = recupDerniereAssiduite(etud.assiduites);
  prevDateAssi.classList.add(lastAssiduite?.etat.toLowerCase() ?? "vide");
  setupAssiduiteBubble(prevDateAssi, lastAssiduite);
  assiduitesBar.appendChild(prevDateAssi);

  // div minitimeline
  assiduitesBar.appendChild(createMiniTimeline(etud.assiduites));
  ligneEtud.appendChild(assiduitesBar);

  // fieldset btns_field single
  const btnsField = document.createElement("fieldset");
  btnsField.classList.add("btns_field", "single");
  btnsField.setAttribute("etudid", etud.id);
  btnsField.setAttribute("type", currentAssiduite.type);
  btnsField.setAttribute("assiduite_id", currentAssiduite.assiduite_id);

  // Création des boutons d'assiduités
  if (readOnly) {
  } else if (currentAssiduite.type != "conflit") {
    ["present", "retard", "absent"].forEach((abs) => {
      const btn = document.createElement("input");
      btn.type = "checkbox";
      btn.value = abs;
      btn.name = `btn_assiduites_${index}`;
      btn.id = `rbtn_${abs}`;
      btn.classList.add("rbtn", abs);
      btn.title = abs;

      btn.checked = abs === currentAssiduite?.etat.toLowerCase();

      // Une seule checkbox à la fois
      btn.addEventListener("click", () => {
        Array.from(btn.parentElement.children).forEach((chbox) => {
          if (chbox.checked && chbox.value !== btn.value) {
            chbox.checked = false;
          }
        });
      });

      // Action au clic
      btn.addEventListener("click", (e) => {
        actionAssiduite(
          etud,
          btn.value,
          currentAssiduite.type,
          currentAssiduite.type == "edition" ? currentAssiduite : null
        );
        e.preventDefault();
      });

      btnsField.appendChild(btn);
    });
  } else {
    const btn = document.createElement("input");
    btn.type = "checkbox";
    btn.value = "conflit";
    btn.name = `btn_assiduites_${index}`;
    btn.id = `rbtn_conflit`;
    btn.classList.add("rbtn", "conflit");
    btn.title = "conflit";

    // TODO : Ouvrir solveur

    const solveur = new ConflitResolver(etud.assiduites, getPeriodAsDate(), {
      deb: $("#date").datepicker("getDate"),
      fin: $("#date").datepicker("getDate").add(1, "days"),
    });

    const update = () => {
      MiseAJourLigneEtud(etud);
    };

    solveur.callbacks = {
      delete: update,
      edit: update,
      split: update,
    };

    btn.addEventListener("click", () => {
      solveur.open();
      btn.checked = false;
    });

    btnsField.appendChild(btn);
  }

  ligneEtud.appendChild(btnsField);

  return ligneEtud;
}

/**
 * Génération de toutes les lignes étudiantes
 */

async function creerTousLesEtudiants(etuds) {
  const etudsDiv = document.querySelector(".etud_holder");
  etudsDiv.innerHTML = "";
  const moduleImplId = readOnly ? null : $("#moduleimpl_select").val();
  const inscriptions = await getInscriptionModule(moduleImplId);
  [...etuds.values()]
    .sort((a, b) => {
      return a.sort_key > b.sort_key ? 1 : -1;
    })
    .filter((etud) => {
      return inscriptions == null || inscriptions.includes(etud.id);
    })
    .forEach((etud, index) => {
      etudsDiv.appendChild(creerLigneEtudiant(etud, index + 1));
    });
}

/**
 * Récupère une version lisible du moduleimpl
 * @param {Object} assiduite
 * @returns {String}
 */
async function getModuleImpl(assiduite) {
  if (assiduite == null) return "Pas de module";
  const id = assiduite.moduleimpl_id;

  if (id == null || id == undefined) {
    if (
      assiduite.hasOwnProperty("external_data") &&
      assiduite.external_data != null &&
      assiduite.external_data.hasOwnProperty("module")
    ) {
      return assiduite.external_data.module == "Autre"
        ? "Autre module (pas dans la liste)"
        : assiduite.external_data.module;
    } else {
      return "Pas de module";
    }
  }

  if (id in moduleimpls) {
    return moduleimpls[id];
  }
  const url_api = `../../api/moduleimpl/${id}`;

  return await fetch(url_api)
    .then((res) => {
      if (!res.ok) {
        throw new Error("Network response was not ok");
      }
      return res.json();
    })
    .then((data) => {
      moduleimpls[id] = `${data.module.code} ${data.module.abbrev}`;
      return moduleimpls[id];
    })
    .catch((_) => {
      moduleimpls[id] = "Pas de module";
      return moduleimpls[id];
    });
}
/**
 * Renvoie le moduleimpl_id de l'assiduité
 * ou l'external_data.module si le moduleimpl_id n'est pas défini
 * "" si aucun module n'est défini
 * @param {Object} assiduite
 * @returns {String}
 */
function getModuleImplId(assiduite) {
  const id = assiduite.moduleimpl_id;
  if (id == null || id == undefined) {
    if (
      assiduite.hasOwnProperty("external_data") &&
      assiduite.external_data != null &&
      assiduite.external_data.hasOwnProperty("module")
    ) {
      return assiduite.external_data.module.toLowerCase();
    } else {
      return "";
    }
  } else {
    return id + "";
  }
}
/**
 * Récupère les etudid de tous les étudiants inscrits au module
 * @param {String} moduleimpl_id
 * @returns {Array}
 */
async function getInscriptionModule(moduleimpl_id) {
  if ([null, "", "autre"].includes(moduleimpl_id)) return null;
  if (!inscriptionsModules.has(moduleimpl_id)) {
    const path = `../../api/moduleimpl/${moduleimpl_id}/inscriptions`;
    await fetch(path)
      .then((res) => {
        if (!res.ok) {
          throw new Error("Network response was not ok");
        }
        return res.json();
      })
      .then((data) => {
        inscriptionsModules.set(
          moduleimpl_id,
          data.map((i) => i.etudid)
        );
      })
      .catch((_) => {
        inscriptionsModules.set(moduleimpl_id, []);
      });
  }

  return inscriptionsModules.get(moduleimpl_id);
}

async function MiseAJourLigneEtud(etud) {
  //Récupérer ses assiduités

  function RecupAssiduitesEtudiant(etudid) {
    const date = $("#date").datepicker("getDate");
    const date_debut = date.add(-1, "days").format("YYYY-MM-DDTHH:mm");
    const date_fin = date.add(2, "days").format("YYYY-MM-DDTHH:mm");
    url =
      `../../api/assiduites/${etudid}/query?date_debut=${date_debut}` +
      `&date_fin=${date_fin}&with_justifs`;

    return fetch(url)
      .then((res) => {
        if (!res.ok) {
          throw new Error("Network response was not ok");
        }
        return res.json();
      })
      .then((data) => {
        etud.assiduites = data;
      })
      .catch((error) => {
        console.error(
          "There has been a problem with your fetch operation:",
          error
        );
      });
  }

  await RecupAssiduitesEtudiant(etud.id);

  const etudRow = document.getElementById(`etud_row_${etud.id}`);
  if (etudRow == null) return;

  const ligneEtud = creerLigneEtudiant(
    etud,
    document.querySelector(`#etud_row_${etud.id}`).querySelector(".index")
      .textContent
  );

  etudRow.replaceWith(ligneEtud);
}

async function actionAssiduite(etud, etat, type, assiduite = null) {
  const modimpl_id = $("#moduleimpl_select").val();
  if (
    assiduite &&
    assiduite.etat.toLowerCase() === etat &&
    assiduite.moduleimpl_id == modimpl_id
  )
    type = "suppression";

  const { deb, fin } = getPeriodAsDate();

  let assiduiteObjet = assiduite ?? {
    date_debut: deb,
    date_fin: fin,
    etudid: etud.id,
  };

  assiduiteObjet.etat = etat;
  assiduiteObjet.moduleimpl_id = modimpl_id;

  if (type === "creation") {
    await async_post(
      `../../api/assiduite/${etud.id}/create`,
      [assiduiteObjet],
      (data) => {
        if (data.success.length > 0) {
          MiseAJourLigneEtud(etud);
          envoiToastEtudiant(etat, etud);
        } else {
          console.error(data.errors["0"].message);
          erreurModuleImpl(data.errors["0"].message);
        }
      },
      (error) => {
        console.error("Erreur lors de la création de l'assiduité", error);
      }
    );
  } else if (type === "edition") {
    await async_post(
      `../../api/assiduite/${assiduite.assiduite_id}/edit`,
      {
        etat: assiduiteObjet.etat,
        moduleimpl_id: assiduiteObjet.moduleimpl_id,
      },
      (data) => {
        MiseAJourLigneEtud(etud);
        envoiToastEtudiant(etat, etud);
      },
      (error) => {
        console.error("Erreur lors de la modification de l'assiduité", error);
      }
    );
  } else if (type === "suppression") {
    await async_post(
      `../../api/assiduite/delete`,
      [assiduite.assiduite_id],
      (data) => {
        if (data.success.length > 0) {
          MiseAJourLigneEtud(etud);
          envoiToastEtudiant("remove", etud);
        } else {
          console.error(data.errors["0"].message);
          erreurModuleImpl(data.errors["0"].message);
        }
      },
      (error) => {
        console.error("Erreur lors de la suppression de l'assiduité", error);
      }
    );
  }
}

function erreurModuleImpl(message) {
  if (message == "Module non renseigné") {
    const HTML = `
  <p>Attention, le module doit obligatoirement être renseigné.</p>
  <p>Cela vient de la configuration du semestre ou plus largement du département.</p>
  <p>Si c'est une erreur, veuillez voir avec le ou les responsables de votre scodoc.</p>
  `;

    const content = document.createElement("div");
    content.innerHTML = HTML;

    openAlertModal("Sélection du module", content);
  }
  if (message == "L'étudiant n'est pas inscrit au module") {
    const HTML = `
  <p>Attention, l'étudiant n'est pas inscrit à ce module.</p>
  <p>Si c'est une erreur, veuillez voir avec le ou les responsables de votre scodoc.</p>
  `;

    const content = document.createElement("div");
    content.innerHTML = HTML;

    openAlertModal("Sélection du module", content);
  }
}

function mettreToutLeMonde(etat, el = null) {
  const lignesEtuds = [...document.querySelectorAll("fieldset.btns_field")];

  const { deb, fin } = getPeriodAsDate();
  const assiduiteObjet = {
    date_debut: deb,
    date_fin: fin,
    etat: etat,
    moduleimpl_id: $("#moduleimpl_select").val(),
  };

  if (el != null) el.checked = false;

  // Suppression des assiduités
  if (etat == "vide") {
    const assiduites_id = lignesEtuds
      .filter((e) => e.getAttribute("type") == "edition")
      .map((e) => Number(e.getAttribute("assiduite_id")));
    afficheLoader();

    async_post(
      `../../api/assiduite/delete`,
      assiduites_id,
      async (data) => {
        retirerLoader();
        if (data.errors.length == 0) {
          await recupAssiduites(etuds, $("#date").datepicker("getDate"));
          creerTousLesEtudiants(etuds);
        } else {
          console.error(data.errors);
        }
        envoiToastTous("remove", assiduites_id.length);
      },
      (error) => {
        console.error("Erreur lors de la suppression de l'assiduité", error);
      }
    );

    return;
  }

  // Création / édition des assiduités
  const assiduitesACreer = lignesEtuds
    .filter((e) => e.getAttribute("type") == "creation")
    .map((e) => Number(e.getAttribute("etudid")));
  const assiduitesAEditer = lignesEtuds
    .filter((e) => e.getAttribute("type") == "edition")
    .map((e) => Number(e.getAttribute("assiduite_id")));

  // création

  const promiseCreate = async_post(
    `../../api/assiduites/create`,
    assiduitesACreer.map((etudid) => {
      return { ...assiduiteObjet, etudid };
    }),
    async (data) => {
      if (data.errors.length > 0) {
        console.error(data.errors);
      }
    },
    (error) => {
      console.error("Erreur lors de la création de l'assiduité", error);
    }
  );
  const promiseEdit = async_post(
    `../../api/assiduites/edit`,
    assiduitesAEditer.map((assiduite_id) => {
      return { ...assiduiteObjet, assiduite_id };
    }),
    async (data) => {
      if (data.errors.length > 0) {
        console.error(data.errors);
      }
    },
    (error) => {
      console.error("Erreur lors de l'édition de l'assiduité", error);
    }
  );

  // Affiche un loader
  afficheLoader();

  Promise.all([promiseCreate, promiseEdit]).then(async () => {
    retirerLoader();
    await recupAssiduites(etuds, $("#date").datepicker("getDate"));
    creerTousLesEtudiants(etuds);
    envoiToastTous(etat, assiduitesACreer.length + assiduitesAEditer.length);
  });
}

function afficheLoader() {
  const loaderDiv = document.createElement("div");
  loaderDiv.id = "loader";
  const span = document.createElement("span");
  span.textContent = "Chargement en cours";
  loaderDiv.appendChild(span);
  const loader = document.createElement("div");
  loader.classList.add("loader");
  loaderDiv.appendChild(loader);
  document.body.appendChild(loaderDiv);
}

function retirerLoader() {
  document.getElementById("loader").remove();
}

function envoiToastEtudiant(etat, etud) {
  let etatAffiche;

  switch (etat.toUpperCase()) {
    case "PRESENT":
      etatAffiche = "%etud% a été noté(e) <u><strong>présent(e)</strong></u>";
      break;
    case "RETARD":
      etatAffiche = "%etud% a été noté(e) <u><strong>en retard</strong></u>";
      break;
    case "ABSENT":
      etatAffiche = "%etud% a été noté(e) <u><strong>absent(e)</strong></u>";
      break;
    case "REMOVE":
      etatAffiche = "L'assiduité de %etud% a été retirée.";
  }

  const nom_prenom = `${etud.nom.toUpperCase()} ${etud.prenom.capitalize()}`;
  const span = document.createElement("span");
  span.innerHTML = etatAffiche.replace("%etud%", nom_prenom);

  pushToast(generateToast(span, getToastColorFromEtat(etat.toUpperCase()), 5));
}

function envoiToastTous(etat, count) {
  const span = document.createElement("span");
  let etatAffiche = etat;
  switch (etat) {
    case "remove":
      if (count > 0) {
        span.innerHTML = `${count} assiduités ont été supprimées.`;
      } else {
        span.innerHTML = `Aucune assiduité n'a été supprimée.`;
      }
      break;
    case "retard":
      etatAffiche = "En retard";
    default:
      if (count > 0) {
        span.innerHTML = `${count} étudiants ont été mis <u><strong>${etatAffiche
          .capitalize()
          .trim()}</strong></u>`;
      } else {
        span.innerHTML = `Aucun étudiant n'a été mis <u><strong>${etatAffiche
          .capitalize()
          .trim()}</strong></u>`;
      }
      break;
  }

  pushToast(generateToast(span, getToastColorFromEtat(etat.toUpperCase()), 5));
}

function estJourTravail(jour, nonWorkdays) {
  const d = Intl.DateTimeFormat("fr-FR", {
    timeZone: SCO_TIMEZONE,
    weekday: "short",
  })
    .format(jour)
    .replace(".", "");
  return !nonWorkdays.includes(d);
}

function retourJourTravail(date) {
  const jourMiliSecondes = 86400000; // 24 * 3600 * 1000 | H * s * ms
  let jour = date;
  let compte = 0;

  while (!estJourTravail(jour, nonWorkDays) && compte++ < 7) {
    jour = new Date(jour - jourMiliSecondes);
  }
  return jour;
}

function dateCouranteEstTravaillee() {
  const date = $("#date").datepicker("getDate");
  if (!estJourTravail(date, nonWorkDays)) {
    const nouvelleDate = retourJourTravail(date);
    $("#date").datepicker("setDate", nouvelleDate);

    const att = document.createTextNode(
      `Le jour sélectionné (${Date.toFRA(
        date.format("YYYY-MM-DD")
      )}) n'est pas un jour travaillé.`
    );
    const div = document.createElement("div");
    div.appendChild(att);
    div.appendChild(document.createElement("br"));
    div.appendChild(
      document.createTextNode(
        `Le dernier jour travaillé disponible a été sélectionné : ${Date.toFRA(
          nouvelleDate.format("YYYY-MM-DD")
        )}.`
      )
    );
    openAlertModal("Attention", div, "", "#eec660");

    return false;
  }
  return true;
}

/**
 * Ajout de la visualisation des assiduités de la mini timeline
 * @param {HTMLElement} el l'élément survollé
 * @param {Assiduité} assiduite l'assiduité représentée par l'élément
 */
function setupAssiduiteBubble(el, assiduite) {
  function formatDateModal(dateStr) {
    const date = new Date(Date.removeUTC(dateStr));
    return date.format("DD/MM/Y HH:mm");
  }

  if (!assiduite) return;

  const bubble = document.createElement("div");
  bubble.className = "assiduite-bubble";
  bubble.classList.add(assiduite.etat.toLowerCase());

  // Ajout d'un lien pour plus d'informations
  const infos = document.createElement("a");
  infos.className = "";
  infos.textContent = `ℹ️`;
  infos.title = "Cliquez pour plus d'informations";
  infos.target = "_blank";
  infos.href = `tableau_assiduite_actions?type=assiduite&action=details&obj_id=${assiduite.assiduite_id}`;

  // Ajout d'un lien pour modifier l'assiduité
  const modifs = document.createElement("a");
  modifs.className = "";
  modifs.textContent = `📝`;
  modifs.title = "Cliquez pour modifier l'assiduité";
  modifs.target = "_blank";
  modifs.href = `tableau_assiduite_actions?type=assiduite&action=modifier&obj_id=${assiduite.assiduite_id}`;

  const actionsDiv = document.createElement("div");
  actionsDiv.className = "assiduite-actions";
  actionsDiv.appendChild(modifs);
  actionsDiv.appendChild(infos);
  bubble.appendChild(actionsDiv);

  const idDiv = document.createElement("div");
  idDiv.className = "assiduite-id";
  getModuleImpl(assiduite).then((modImpl) => {
    idDiv.textContent = `${modImpl}`;
  });
  bubble.appendChild(idDiv);

  const periodDivDeb = document.createElement("div");
  periodDivDeb.className = "assiduite-period";
  periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`;
  bubble.appendChild(periodDivDeb);
  const periodDivFin = document.createElement("div");
  periodDivFin.className = "assiduite-period";
  periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`;
  bubble.appendChild(periodDivFin);

  const stateDiv = document.createElement("div");
  stateDiv.className = "assiduite-state";
  stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`;
  bubble.appendChild(stateDiv);

  const motifDiv = document.createElement("div");
  stateDiv.className = "assiduite-why";
  const motif = ["", null, undefined].includes(assiduite.desc)
    ? "Pas de motif"
    : assiduite.desc.capitalize();
  stateDiv.textContent = `Motif: ${motif}`;
  bubble.appendChild(motifDiv);

  const userIdDiv = document.createElement("div");
  userIdDiv.className = "assiduite-user_id";
  userIdDiv.textContent = `saisie le ${formatDateModal(
    assiduite.entry_date,
    " à "
  )}`;

  if (assiduite.user_id != null) {
    userIdDiv.textContent += `\npar ${assiduite.user_nom_complet}`;
  }
  bubble.appendChild(userIdDiv);

  el.appendChild(bubble);
}

/**
 * Permet d'afficher ou non les photos des étudiants
 * @param {boolean} checked
 */
function afficherPDP(checked) {
  if (checked) {
    gtrcontent.setAttribute("data-pdp", "true");
  } else {
    gtrcontent.removeAttribute("data-pdp");
  }

  // On sauvegarde le choix dans le localStorage
  localStorage.setItem("scodoc-etud-pdp", `${checked}`);
  pdp.checked = checked;
}
