// <=== CONSTANTS and GLOBALS ===>

const TIMEZONE = "Europe/Paris";
let url;

function getUrl() {
  if (!url) {
    url = SCO_URL.substring(0, SCO_URL.lastIndexOf("/"));
  }
  return url;
}

//Les valeurs par défaut de la timeline (8h -> 18h)
let currentValues = [8.0, 10.0];

//Objet stockant les étudiants et les assiduités
let etuds = {};
let assiduites = {};
let justificatifs = {};

// Variable qui définit si le processus d'action de masse est lancé
let currentMassAction = false;

/**
 * Variable de gestion des conflits
 */
let modal;
let closeBtn;
let timeline;
let deleteBtn;
let splitBtn;
let editBtn;
let selectedAssiduite;

/**
 * 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,
});
// <<== Outils ==>>

/**
 * Ajout des évents sur les boutons d'assiduité
 * @param {Document | HTMLFieldSetElement} parent par défaut le document, un field sinon
 */
function setupCheckBox(parent = document) {
  const checkboxes = Array.from(parent.querySelectorAll(".rbtn"));
  checkboxes.forEach((box) => {
    box.addEventListener("click", (event) => {
      if (!uniqueCheckBox(box)) {
        event.preventDefault();
      }
      if (!box.parentElement.classList.contains("mass")) {
        assiduiteAction(box);
      }
    });
  });
}

/**
 * Validation préalable puis désactivation des chammps :
 * - Groupe
 * - Module impl
 * - Date
 */
function validateSelectors() {
  const action = () => {
    const group_ids = getGroupIds();

    etuds = {};
    group_ids.forEach((group_id) => {
      sync_get(
        getUrl() + `/api/group/${group_id}/etudiants`,
        (data, status) => {
          if (status === "success") {
            data.forEach((etud) => {
              if (!(etud.id in etuds)) {
                etuds[etud.id] = etud;
              }
            });
          }
        }
      );
    });

    if (getModuleImplId() == null && forceModule) {
      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);
      return;
    }

    getAssiduitesFromEtuds(true);

    document.querySelector(".selectors").disabled = true;
    generateMassAssiduites();
    generateAllEtudRow();
  };

  if (!verifyDateInSemester()) {
    const HTML = `
    <p>Attention, la date sélectionnée n'est pas comprise dans le semestre.</p>
    <p>Cette page permet l'affichage et la modification des assiduités uniquement pour le semestre sélectionné.</p>
    <p>Vous n'aurez donc pas accès aux assiduités.</p>
    <p>Appuyer sur "Valider" uniquement si vous souhaitez poursuivre sans modifier la date.</p>
     `;

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

    openPromptModal("Vérification de la date", content, action);
    return;
  }

  action();
}

/**
 * Limite le nombre de checkbox marquée
 * Vérifie aussi si le cliqué est fait sur des assiduités conflictuelles
 * @param {HTMLInputElement} box la checkbox utilisée
 * @returns {boolean} Faux si il y a un conflit d'assiduité, Vrai sinon
 */
function uniqueCheckBox(box) {
  const type = box.parentElement.getAttribute("type") === "conflit";
  if (!type) {
    const checkboxs = Array.from(box.parentElement.children);

    checkboxs.forEach((chbox) => {
      if (chbox.checked && chbox.value !== box.value) {
        chbox.checked = false;
      }
    });
    return true;
  }

  return false;
}

/**
 * Fait une requête GET de façon synchrone
 * @param {String} path adresse distante
 * @param {CallableFunction} success fonction à effectuer en cas de succès
 * @param {CallableFunction} errors fonction à effectuer en cas d'échec
 */
function sync_get(path, success, errors) {
  $.ajax({
    async: false,
    type: "GET",
    url: path,
    success: success,
    error: errors,
  });
}
/**
 * Fait une requête POST de façon synchrone
 * @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
 */
function sync_post(path, data, success, errors) {
  $.ajax({
    async: false,
    type: "POST",
    url: path,
    data: JSON.stringify(data),
    success: success,
    error: errors,
  });
}
// <<== Gestion des actions de masse ==>>
const massActionQueue = new Map();

/**
 * Cette fonction remet à zero la gestion des actions de masse
 */
function resetMassActionQueue() {
  massActionQueue.set("supprimer", []);
  massActionQueue.set("editer", []);
  massActionQueue.set("creer", []);
}

/**
 * Fonction pour alimenter la queue des actions de masse
 * @param {String} type Le type de queue ("creer", "supprimer", "editer")
 * @param {*} obj L'objet qui sera utilisé par les API
 */
function addToMassActionQueue(type, obj) {
  massActionQueue.get(type)?.push(obj);
}

/**
 * Fonction pour exécuter les actions de masse
 */
function executeMassActionQueue() {
  if (!currentMassAction) return;

  //Récupération des queues
  const toCreate = massActionQueue.get("creer");
  const toEdit = massActionQueue.get("editer");
  const toDelete = massActionQueue.get("supprimer");

  //Fonction qui créé les assidutiés de la queue "creer"
  const create = () => {
    /**
     * Création du template de l'assiduité
     *
     * {
     *   date_debut: #debut_timeline,
     *   date_fin: #fin_timeline,
     *   moduleimpl_id ?: <>
     * }
     */
    const tlTimes = getTimeLineTimes();
    const assiduite = {
      date_debut: tlTimes.deb.format(),
      date_fin: tlTimes.fin.format(),
    };
    const moduleimpl = getModuleImplId();

    if (moduleimpl !== null) {
      assiduite["moduleimpl_id"] = moduleimpl;
    }

    const createQueue = []; //liste des assiduités qui seront créées.

    /**
     * Pour chaque état de la queue 'creer' on génère une
     * assiduitée précise depuis le template
     */
    toCreate.forEach((obj) => {
      const curAssiduite = structuredClone(assiduite);
      curAssiduite.etudid = obj.etudid;
      curAssiduite.etat = obj.etat;

      createQueue.push(curAssiduite);
    });

    /**
     * On envoie les données à l'API
     */
    const path = getUrl() + `/api/assiduites/create`;
    sync_post(
      path,
      createQueue,
      (data, status) => {
        //success
      },
      (data, status) => {
        //error
        console.error(data, status);
      }
    );
  };

  //Fonction qui modifie les assiduités de la queue 'edition'
  const edit = () => {
    //On ajoute le moduleimpl (s'il existe) aux assiduités à modifier
    const editQueue = toEdit.map((assiduite) => {
      const moduleimpl = getModuleImplId();
      if (moduleimpl !== null) {
        assiduite["moduleimpl_id"] = moduleimpl;
      }
      return assiduite;
    });

    const path = getUrl() + `/api/assiduites/edit`;
    sync_post(
      path,
      editQueue,
      (data, status) => {
        //success
      },
      (data, status) => {
        //error
        console.error(data, status);
      }
    );
  };

  //Fonction qui supprime les assiduités de la queue 'supprimer'
  const supprimer = () => {
    const path = getUrl() + `/api/assiduite/delete`;
    sync_post(
      path,
      toDelete,
      (data, status) => {
        //success
      },
      (data, status) => {
        //error
        console.error(data, status);
      }
    );
  };

  //On exécute les fonctions de queue
  create();
  edit();
  supprimer();
  //On récupère les assiduités puis on regénère les lignes d'étudiants
  getAssiduitesFromEtuds(true);
  generateAllEtudRow();
}
/**
 * Processus de peuplement des queues
 * puis d'exécution
 */
function massAction() {
  //On récupère tous les boutons d'assiduités
  const fields = Array.from(document.querySelectorAll(".btns_field.single"));
  //On récupère l'état de l'action de masse
  const action = getAssiduiteValue(document.querySelector(".btns_field.mass"));
  //On remet à 0 les queues
  resetMassActionQueue();

  //on met à vrai la variable pour la suite
  currentMassAction = true;

  //On affiche le "loader" le temps du processus
  showLoader();

  //On timeout 0 pour le mettre à la fin de l'event queue de JS
  setTimeout(() => {
    const conflicts = [];
    /**
     * Pour chaque étudiant :
     * On vérifie s'il y a un conflit -> on place l'étudiant dans l'array conflicts
     * Sinon -> on fait comme si l'utilisateur cliquait sur le bouton d'assiduité
     */
    fields.forEach((field) => {
      if (field.getAttribute("type") != "conflit") {
        field.querySelector(`.rbtn.${action}`).click();
      } else {
        const etudid = field.getAttribute("etudid");
        conflicts.push(etuds[parseInt(etudid)]);
      }
    });

    //on exécute les queues puis on cache le loader
    executeMassActionQueue();
    hideLoader();

    //Fin du processus, on remet à false
    currentMassAction = false;

    //On remet à zero les boutons d'assiduité de masse
    const boxes = Array.from(
      document.querySelector(".btns_field.mass").querySelectorAll(".rbtn")
    );
    boxes.forEach((box) => {
      box.checked = false;
    });

    //Si il y a des conflits d'assiduité, on affiche la liste dans une alert
    if (conflicts.length > 0) {
      const div = document.createElement("div");
      const sub = document.createElement("p");
      sub.textContent =
        "L'assiduité des étudiants suivant n'a pas pu être modifiée";
      div.appendChild(sub);
      const ul = document.createElement("ul");
      conflicts.forEach((etu) => {
        const li = document.createElement("li");
        li.textContent = `${etu.nom} ${etu.prenom.capitalize()}`;
        ul.appendChild(li);
      });
      div.appendChild(ul);
      openAlertModal("Conflits d'assiduités", div, "");
    }
  }, 0);
}

/**
 * On génère les boutons d'assiduités de masse
 * puis on ajoute les événements associés
 */
function generateMassAssiduites() {
  const content = document.getElementById("content");

  const mass = document.createElement("div");
  mass.className = "mass-selection";
  mass.innerHTML = `
    <span>Mettre tout le monde :</span>
    <fieldset class="btns_field mass">
        <input type="checkbox" value="present" name="mass_btn_assiduites" id="mass_rbtn_present"
            class="rbtn present">
        <input type="checkbox" value="retard" name="mass_btn_assiduites" id="mass_rbtn_retard" class="rbtn retard">
        <input type="checkbox" value="absent" name="mass_btn_assiduites" id="mass_rbtn_absent" class="rbtn absent">
    </fieldset>`;

  content.insertBefore(mass, content.querySelector(".etud_holder"));

  const mass_btn = Array.from(mass.querySelectorAll(".rbtn"));
  mass_btn.forEach((btn) => {
    btn.addEventListener("click", () => {
      massAction();
    });
  });

  if (!verifyDateInSemester()) {
    content.querySelector(".btns_field.mass").setAttribute("disabled", "true");
  }
}

/**
 * Affichage du loader
 */
function showLoader() {
  document.getElementById("loaderContainer").style.display = "block";
}
/**
 * Dissimulation du loader
 */
function hideLoader() {
  document.getElementById("loaderContainer").style.display = "none";
}

// <<== Gestion du temps ==>>

/**
 * Transforme un temps numérique en string
 * 8.75 -> 08h45
 * @param {number} time Le temps (float)
 * @returns {string} le temps (string)
 */
function toTime(time) {
  let heure = Math.floor(time);
  let minutes = (time - heure) * 60;
  if (minutes < 1) {
    minutes = "00";
  }
  if (heure < 10) {
    heure = `0${heure}`;
  }
  return `${heure}h${minutes}`;
}
/**
 * Transforme une date iso en une date lisible:
 * new Date('2023-03-03') -> "vendredi 3 mars 2023"
 * @param {Date} date
 * @param {object} styles
 * @returns
 */
function formatDate(date, styles = { dateStyle: "full" }) {
  return new Intl.DateTimeFormat("fr-FR", styles).format(date);
}

/**
 * Met à jour la date visible sur la page en la formatant
 */
function updateDate() {
  const dateInput = document.querySelector("#tl_date");

  const date = dateInput.valueAsDate;

  $("#datestr").text(formatDate(date).capitalize());
}

function verifyDateInSemester() {
  const date = new moment.tz(
    document.querySelector("#tl_date").value,
    TIMEZONE
  );

  const periodSemester = getFormSemestreDates();

  return date.isBetween(periodSemester.deb, periodSemester.fin);
}

/**
 * Ajoute la possibilité d'ouvrir le calendrier
 * lorsqu'on clique sur la date
 */
function setupDate(onchange = null) {
  const datestr = document.querySelector("#datestr");
  const input = document.querySelector("#tl_date");

  datestr.addEventListener("click", () => {
    if (!input.disabled) {
      input.showPicker();
    }
  });

  if (onchange != null) {
    input.addEventListener("change", onchange);
  }
}

/**
 * GetAssiduitesOnDateChange
 * (Utilisé uniquement avec étudiant unique)
 */

function getAssiduitesOnDateChange() {
  if (!isSingleEtud()) return;
  actualizeEtud(etudid);
}
/**
 * Transforme une date iso en date intelligible
 * @param {String} str date iso
 * @param {String} separator le séparateur de la date intelligible (01/01/2000 {separtor} 10:00)
 * @returns {String} la date intelligible
 */
function formatDateModal(str, separator = "·") {
  return new moment.tz(str, TIMEZONE).format(`DD/MM/Y ${separator} HH:mm`);
}

/**
 * Fonction qui vérifie si une période est dans un interval
 * Objet période / interval
 * {
 *      deb: moment.tz(<Date>),
 *      fin: moment.tz(<Date>),
 * }
 * @param {object} period
 * @param {object} interval
 * @returns {boolean} Vrai si la période est dans l'interval
 */
function hasTimeConflict(period, interval) {
  return period.deb.isBefore(interval.fin) && period.fin.isAfter(interval.deb);
}

/**
 * On récupère la période de la timeline
 * @returns {deb : moment.tz(), fin: moment.tz()}
 */
function getTimeLineTimes() {
  //getPeriodValues() -> retourne la position de la timeline [a,b] avec a et b des number
  let values = getPeriodValues();
  //On récupère la date
  const dateiso = document.querySelector("#tl_date").value;

  //On génère des objets temps (moment.tz)
  values = values.map((el) => {
    el = toTime(el).replace("h", ":");
    el = `${dateiso}T${el}`;
    return moment.tz(el, TIMEZONE);
  });

  return { deb: values[0], fin: values[1] };
}

/**
 * Vérification de l'égalité entre un conflit et la période de la timeline
 * @param {object} conflict
 * @returns {boolean} Renvoie Vrai si la période de la timeline est égal au conflit
 */
function isConflictSameAsTimeLine(conflict) {
  const tlTimes = getTimeLineTimes();
  const clTimes = {
    deb: moment.tz(conflict.date_debut, TIMEZONE),
    fin: moment.tz(conflict.date_fin, TIMEZONE),
  };
  return tlTimes.deb.isSame(clTimes.deb) && tlTimes.fin.isSame(clTimes.fin);
}

/**
 * Retourne un objet Date de la date sélectionnée
 * @returns {Date} la date sélectionnée
 */
function getDate() {
  const date = document.querySelector("#tl_date").valueAsDate;
  date.setHours(0, 0, 0, 0);
  return date;
}

/**
 * Retourne un objet date représentant le jour suivant
 * @returns {Date} le jour suivant
 */
function getNextDate() {
  const date = getDate();
  const next = new Date(date.valueOf());
  next.setDate(date.getDate() + 1);
  next.setHours(0, 0, 0, 0);
  return next;
}
/**
 * Retourne un objet date représentant le jour précédent
 * @returns {Date} le jour précédent
 */
function getPrevDate() {
  const date = getDate();
  const next = new Date(date.valueOf());
  next.setDate(date.getDate() - 1);
  next.setHours(0, 0, 0, 0);
  return next;
}

/**
 * Transformation d'un objet Date en chaîne ISO
 * @param {Date} date
 * @returns {string} la date iso avec le timezone
 */
function toIsoString(date) {
  var tzo = -date.getTimezoneOffset(),
    dif = tzo >= 0 ? "+" : "-",
    pad = function (num) {
      return (num < 10 ? "0" : "") + num;
    };

  return (
    date.getFullYear() +
    "-" +
    pad(date.getMonth() + 1) +
    "-" +
    pad(date.getDate()) +
    "T" +
    pad(date.getHours()) +
    ":" +
    pad(date.getMinutes()) +
    ":" +
    pad(date.getSeconds()) +
    dif +
    pad(Math.floor(Math.abs(tzo) / 60)) +
    ":" +
    pad(Math.abs(tzo) % 60)
  );
}

/**
 * Transforme un temps numérique en une date moment.tz
 * @param {number} nb
 * @returns {moment.tz} Une date formée du temps donné et de la date courante
 */
function numberTimeToDate(nb) {
  time = toTime(nb).replace("h", ":");
  date = document.querySelector("#tl_date").value;

  datetime = `${date}T${time}`;

  return moment.tz(datetime, TIMEZONE);
}

// <<== Gestion des assiduités ==>>

/**
 * Récupère les assiduités des étudiants
 * en fonction de :
 *   - du semestre
 *   - de la date courant et du jour précédent.
 * @param {boolean} clear vidage de l'objet "assiduites" ou non
 * @returns {object} l'objets Assiduités {<etudid:str> : [<assiduite>,]}
 */
function getAssiduitesFromEtuds(clear, has_formsemestre = true) {
  const etudIds = Object.keys(etuds).join(",");
  const formsemestre_id = has_formsemestre
    ? `formsemestre_id=${getFormSemestreId()}&`
    : "";

  const date_debut = toIsoString(getPrevDate());
  const date_fin = toIsoString(getNextDate());

  if (clear) {
    assiduites = {};
  }

  const url_api =
    getUrl() +
    `/api/assiduites/group/query?date_debut=${formsemestre_id}${date_debut}&date_fin=${date_fin}&etudids=${etudIds}`;
  sync_get(url_api, (data, status) => {
    if (status === "success") {
      const dataKeys = Object.keys(data);
      dataKeys.forEach((key) => {
        assiduites[key] = data[key];
      });
    }
  });
  return assiduites;
}

/**
 * Création d'une assiduité pour un étudiant
 * @param {String} etat l'état de l'étudiant
 * @param {Number | String} etudid l'identifiant de l'étudiant
 *
 * TODO : Rendre asynchrone
 */
function createAssiduite(etat, etudid) {
  const tlTimes = getTimeLineTimes();
  const assiduite = {
    date_debut: tlTimes.deb.format(),
    date_fin: tlTimes.fin.format(),
    etat: etat,
  };

  const moduleimpl = getModuleImplId();

  if (moduleimpl !== null) {
    assiduite["moduleimpl_id"] = moduleimpl;
  }

  const path = getUrl() + `/api/assiduite/${etudid}/create`;
  sync_post(
    path,
    [assiduite],
    (data, status) => {
      //success
      if (data.success.length > 0) {
        let obj = data.success["0"].assiduite_id;
      }
    },
    (data, status) => {
      //error
      console.error(data, status);
    }
  );
}

/**
 * Suppression d'une assiduité
 * @param {String | Number} assiduite_id l'identifiant de l'assiduité
 * TODO : Rendre asynchrone
 */
function deleteAssiduite(assiduite_id) {
  const path = getUrl() + `/api/assiduite/delete`;
  sync_post(
    path,
    [assiduite_id],
    (data, status) => {
      //success
      if (data.success.length > 0) {
        let obj = data.success["0"].assiduite_id;
      }
    },
    (data, status) => {
      //error
      console.error(data, status);
    }
  );
}

/**
 *
 * @param {String | Number} assiduite_id l'identifiant d'une assiduité
 * @param {String} etat l'état à modifier
 * @returns {boolean} si l'édition a fonctionné
 * TODO : Rendre asynchrone
 */
function editAssiduite(assiduite_id, etat) {
  const assiduite = {
    etat: etat,
    moduleimpl_id: getModuleImplId(),
  };
  const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`;
  let bool = false;
  sync_post(
    path,
    assiduite,
    (data, status) => {
      bool = true;
    },
    (data, status) => {
      //error
      console.error(data, status);
    }
  );

  return bool;
}

/**
 * Récupération des assiduités conflictuelles avec la période de la time line
 * @param {String | Number} etudid identifiant de l'étudiant
 * @returns {Array[Assiduité]} un tableau d'assiduité
 */
function getAssiduitesConflict(etudid) {
  const etudAssiduites = assiduites[etudid];
  if (!etudAssiduites) {
    return [];
  }
  const period = getTimeLineTimes();
  return etudAssiduites.filter((assi) => {
    const interval = {
      deb: moment.tz(assi.date_debut, TIMEZONE),
      fin: moment.tz(assi.date_fin, TIMEZONE),
    };
    return hasTimeConflict(period, interval);
  });
}

/**
 * Récupération de la dernière assiduité du jour précédent
 * @param {String | Number} etudid l'identifiant de l'étudiant
 * @returns {Assiduité} la dernière assiduité du jour précédent
 */
function getLastAssiduiteOfPrevDate(etudid) {
  const etudAssiduites = assiduites[etudid];
  if (!etudAssiduites) {
    return "";
  }
  const period = {
    deb: moment.tz(getPrevDate(), TIMEZONE),
    fin: moment.tz(getDate(), TIMEZONE),
  };
  const prevAssiduites = etudAssiduites
    .filter((assi) => {
      const interval = {
        deb: moment.tz(assi.date_debut, TIMEZONE),
        fin: moment.tz(assi.date_fin, TIMEZONE),
      };

      return hasTimeConflict(period, interval);
    })
    .sort((a, b) => {
      const a_fin = moment.tz(a.date_fin, TIMEZONE);
      const b_fin = moment.tz(b.date_fin, TIMEZONE);
      return b_fin < a_fin;
    });

  if (prevAssiduites.length < 1) {
    return null;
  }

  return prevAssiduites.pop();
}

/**
 * Récupération de l'état appointé
 * @param {HTMLFieldSetElement} field le conteneur des boutons d'assiduité d'une ligne étudiant
 * @returns {String} l'état appointé : ('present','absent','retard', 'remove')
 *
 * état = 'remove' si le clic désélectionne une assiduité appointée
 */
function getAssiduiteValue(field) {
  const checkboxs = Array.from(field.children);
  let value = "remove";
  checkboxs.forEach((chbox) => {
    if (chbox.checked) {
      value = chbox.value;
    }
  });

  return value;
}

/**
 * Mise à jour des assiduités d'un étudiant
 * @param {String | Number} etudid identifiant de l'étudiant
 */
function actualizeEtudAssiduite(etudid, has_formsemestre = true) {
  const formsemestre_id = has_formsemestre
    ? `formsemestre_id=${getFormSemestreId()}&`
    : "";
  const date_debut = toIsoString(getPrevDate());
  const date_fin = toIsoString(getNextDate());

  const url_api =
    getUrl() +
    `/api/assiduites/${etudid}/query?${formsemestre_id}date_debut=${date_debut}&date_fin=${date_fin}`;

  sync_get(url_api, (data, status) => {
    if (status === "success") {
      assiduites[etudid] = data;
    }
  });
}

function getAllAssiduitesFromEtud(etudid, action) {
  const url_api = getUrl() + `/api/assiduites/${etudid}`;

  $.ajax({
    async: true,
    type: "GET",
    url: url_api,
    success: (data, status) => {
      if (status === "success") {
        assiduites[etudid] = data;
        action(data);
      }
    },
    error: () => {},
  });
}

/**
 * Déclenchement d'une action après appuie sur un bouton d'assiduité
 * @param {HTMLInputElement} element Bouton d'assiduité appuyé
 */
function assiduiteAction(element) {
  const field = element.parentElement;

  const type = field.getAttribute("type");
  const etudid = parseInt(field.getAttribute("etudid"));
  const assiduite_id = parseInt(field.getAttribute("assiduite_id"));
  const etat = getAssiduiteValue(field);

  // Cas de l'action de masse -> peuplement des queues
  if (currentMassAction) {
    switch (type) {
      case "création":
        addToMassActionQueue("creer", { etat: etat, etudid: etudid });
        break;
      case "édition":
        if (etat === "remove") {
          addToMassActionQueue("supprimer", assiduite_id);
        } else {
          addToMassActionQueue("editer", {
            etat: etat,
            assiduite_id: assiduite_id,
          });
        }
        break;
    }
  } else {
    // Cas normal -> mise à jour en base
    switch (type) {
      case "création":
        createAssiduite(etat, etudid);
        break;
      case "édition":
        if (etat === "remove") {
          deleteAssiduite(assiduite_id);
        } else {
          editAssiduite(assiduite_id, etat);
        }
        break;
      case "conflit":
        openModal(assiduites[etudid]);
        break;
    }

    if (type != "conflit") {
      document
        .querySelector(".toast-holder")
        .appendChild(
          generateToast(
            document.createTextNode("L'assiduité a bien été enregistrée.")
          )
        );
    }

    actualizeEtud(etudid, !isSingleEtud);
  }
}

// <<== Gestion de l'affichage des barres étudiant ==>>

/**
 * Génère l'HTML lié à la barre d'un étudiant
 * @param {Etudiant} etud représentation objet d'un étudiant
 * @param {Number} index l'index de l'étudiant dans la liste
 * @param {AssiduitéMod} assiduite Objet représentant l'état de l'étudiant pour la période de la timeline
 * @returns {String} l'HTML généré
 */
function generateEtudRow(
  etud,
  index,
  assiduite = {
    etatAssiduite: "",
    type: "création",
    id: -1,
    date_debut: null,
    date_fin: null,
    prevAssiduites: "",
  }
) {
  // Génération des boutons du choix de l'assiduité
  let assi = "";
  ["present", "retard", "absent"].forEach((abs) => {
    if (abs.toLowerCase() === assiduite.etatAssiduite.toLowerCase()) {
      assi += `<input checked type="checkbox" value="${abs}" name="btn_assiduites_${index}" id="rbtn_${abs}" class="rbtn ${abs}">`;
    } else {
      assi += `<input type="checkbox" value="${abs}" name="btn_assiduites_${index}" id="rbtn_${abs}" class="rbtn ${abs}">`;
    }
  });
  const conflit = assiduite.type == "conflit" ? "conflit" : "";
  const pdp_url = `${getUrl()}/api/etudiant/etudid/${etud.id}/photo?size=small`;
  const HTML = `<div class="etud_row ${conflit}" id="etud_row_${etud.id}">
  
    <div class="index">${index}</div>
    <div class="name_field">
  
        <img class="pdp" src="${pdp_url}">
  
        <div class="name_set">
  
            <h4 class="nom">${etud.nom}</h4>
            <h5 class="prenom">${etud.prenom}</h5>
  
        </div>
  
    </div>
    <div class="assiduites_bar">
        <div id="prevDateAssi" class="${assiduite.prevAssiduites?.etat?.toLowerCase()}">
        <span class="mini_tick">13h</span>
        </div>
    </div>
    <fieldset class="btns_field single" etudid="${etud.id}" assiduite_id="${
    assiduite.id
  }" type="${assiduite.type}">
  
        ${assi}
  
    </fieldset>
    
  
  </div>`;

  return HTML;
}

/**
 * Insertion de la ligne étudiant
 * @param {Etudiant} etud l'objet représentant un étudiant
 * @param {Number} index le n° de l'étudiant dans la liste des étudiants
 * @param {boolean} output ajout automatique dans la page ou non (default : Non)
 * @returns {String} HTML si output sinon rien
 */
function insertEtudRow(etud, index, output = false) {
  const etudHolder = document.querySelector(".etud_holder");
  const conflict = getAssiduitesConflict(etud.id);
  const prevAssiduite = getLastAssiduiteOfPrevDate(etud.id);
  let assiduite = {
    etatAssiduite: "",
    type: "création",
    id: -1,
    date_debut: null,
    date_fin: null,
    prevAssiduites: prevAssiduite,
  };

  if (conflict.length > 0) {
    assiduite.etatAssiduite = conflict[0].etat;

    assiduite.id = conflict[0].assiduite_id;
    assiduite.date_debut = conflict[0].date_debut;
    assiduite.date_fin = conflict[0].date_fin;
    if (isConflictSameAsTimeLine(conflict[0])) {
      assiduite.type = "édition";
    } else {
      assiduite.type = "conflit";
    }
  }
  let row = generateEtudRow(etud, index, assiduite);

  if (output) {
    return row;
  }
  etudHolder.insertAdjacentHTML("beforeend", row);

  row = document.getElementById(`etud_row_${etud.id}`);
  const prev = row.querySelector("#prevDateAssi");
  setupAssiduiteBuble(prev, prevAssiduite);
  const bar = row.querySelector(".assiduites_bar");

  bar.appendChild(createMiniTimeline(assiduites[etud.id]));

  if (!verifyDateInSemester()) {
    row.querySelector(".btns_field.single").setAttribute("disabled", "true");
  }
}

/**
 * Création de la minitiline d'un étudiant
 * @param {Array[Assiduité]} assiduitesArray
 * @returns {HTMLElement} l'élément correspondant à la mini timeline
 */
function createMiniTimeline(assiduitesArray) {
  const array = [...assiduitesArray];
  const dateiso = document.getElementById("tl_date").value;
  const timeline = document.createElement("div");
  timeline.className = "mini-timeline";
  if (isSingleEtud()) {
    timeline.classList.add("single");
  }
  const timelineDate = moment(dateiso).startOf("day");
  const dayStart = timelineDate.clone().add(8, "hours");
  const dayEnd = timelineDate.clone().add(18, "hours");
  const dayDuration = moment.duration(dayEnd.diff(dayStart)).asMinutes();

  const tlTimes = getTimeLineTimes();

  const period_assi = {
    date_debut: tlTimes.deb.format(),
    date_fin: tlTimes.fin.format(),
    etat: "CRENEAU",
  };

  array.push(period_assi);

  array.forEach((assiduité) => {
    const startDate = moment(assiduité.date_debut);
    const endDate = moment(assiduité.date_fin);

    if (startDate.isBefore(dayStart)) {
      startDate.startOf("day").add(8, "hours");
    }

    if (endDate.isAfter(dayEnd)) {
      endDate.startOf("day").add(18, "hours");
    }

    const block = document.createElement("div");
    block.className = "mini-timeline-block";

    const startOffset = moment.duration(startDate.diff(dayStart)).asMinutes();
    const duration = moment.duration(endDate.diff(startDate)).asMinutes();
    const leftPercentage = (startOffset / dayDuration) * 100;
    const widthPercentage = (duration / dayDuration) * 100;

    block.style.left = `${leftPercentage}%`;
    block.style.width = `${widthPercentage}%`;

    if (assiduité.etat != "CRENEAU") {
      if (isSingleEtud()) {
        block.addEventListener("click", () => {
          let deb = startDate.hours() + startDate.minutes() / 60;
          let fin = endDate.hours() + endDate.minutes() / 60;
          deb = Math.max(8, deb);
          fin = Math.min(18, fin);

          setPeriodValues(deb, fin);
          updateSelectedSelect(getCurrentAssiduiteModuleImplId());
          updateJustifyBtn();
        });
      }
      //ajouter affichage assiduites on over
      setupAssiduiteBuble(block, assiduité);
    }

    const action = (justificatifs) => {
      if (justificatifs.length > 0) {
        let j = "invalid_justified";

        justificatifs.forEach((ju) => {
          if (ju.etat == "VALIDE") {
            j = "justified";
          }
        });

        block.classList.add(j);
      }
    };

    if (assiduité.etudid) {
      getJustificatifFromPeriod(
        {
          deb: new moment.tz(assiduité.date_debut, TIMEZONE),
          fin: new moment.tz(assiduité.date_fin, TIMEZONE),
        },
        assiduité.etudid,
        action
      );
    }

    switch (assiduité.etat) {
      case "PRESENT":
        block.classList.add("present");
        break;
      case "RETARD":
        block.classList.add("retard");
        break;
      case "ABSENT":
        block.classList.add("absent");
        break;
      case "CRENEAU":
        block.classList.add("creneau");
        break;
      default:
        block.style.backgroundColor = "white";
    }

    timeline.appendChild(block);
  });

  return timeline;
}

/**
 * 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 setupAssiduiteBuble(el, assiduite) {
  if (!assiduite) return;
  el.addEventListener("mouseenter", (event) => {
    const bubble = document.querySelector(".assiduite-bubble");
    bubble.className = "assiduite-bubble";
    bubble.classList.add("is-active", assiduite.etat.toLowerCase());

    bubble.innerHTML = "";

    const idDiv = document.createElement("div");
    idDiv.className = "assiduite-id";
    idDiv.textContent = `ID: ${assiduite.assiduite_id}`;
    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 userIdDiv = document.createElement("div");
    userIdDiv.className = "assiduite-user_id";
    userIdDiv.textContent = `saisi le ${formatDateModal(
      assiduite.entry_date,
      "à"
    )} \npar ${getUserFromId(assiduite.user_id)}`;
    bubble.appendChild(userIdDiv);

    bubble.style.left = `${event.clientX - bubble.offsetWidth / 2}px`;
    bubble.style.top = `${event.clientY + 20}px`;
  });
  el.addEventListener("mouseout", () => {
    const bubble = document.querySelector(".assiduite-bubble");
    bubble.classList.remove("is-active");
  });
}

/**
 * Mise à jour d'une ligne étudiant
 * @param {String | Number} etudid l'identifiant de l'étudiant
 */
function actualizeEtud(etudid) {
  actualizeEtudAssiduite(etudid, !isSingleEtud());
  //Actualize row
  const etudHolder = document.querySelector(".etud_holder");
  const ancient_row = document.getElementById(`etud_row_${etudid}`);

  let new_row = document.createElement("div");
  new_row.innerHTML = insertEtudRow(
    etuds[etudid],
    ancient_row.querySelector(".index").textContent,
    true
  );
  setupCheckBox(new_row.firstElementChild);
  const bar = new_row.firstElementChild.querySelector(".assiduites_bar");
  bar.appendChild(createMiniTimeline(assiduites[etudid]));
  const prev = new_row.firstElementChild.querySelector("#prevDateAssi");
  if (isSingleEtud()) {
    prev.classList.add("single");
  }
  setupAssiduiteBuble(prev, getLastAssiduiteOfPrevDate(etudid));
  etudHolder.replaceChild(new_row.firstElementChild, ancient_row);
}

/**
 * Génération de toutes les lignes étudiant
 */
function generateAllEtudRow() {
  if (isSingleEtud()) {
    try {
      actualizeEtud(etudid);
    } catch (ignored) {}
    return;
  }

  if (!document.querySelector(".selectors")?.disabled) {
    return;
  }

  document.querySelector(".etud_holder").innerHTML = "";
  etuds_ids = Object.keys(etuds).sort((a, b) =>
    etuds[a].nom > etuds[b].nom ? 1 : etuds[b].nom > etuds[a].nom ? -1 : 0
  );

  for (let i = 0; i < etuds_ids.length; i++) {
    const etud = etuds[etuds_ids[i]];
    insertEtudRow(etud, i + 1);
  }

  setupCheckBox();
}

// <== Gestion du modal de conflit ==>
/**
 * Mise à jour du modal de conflit
 * @param {Array[Assiduité]} assiduiteList Liste des assiduités de l'étudiant
 */
function refreshModal(assiduiteList) {
  const tlTime = getTimeLineTimes();

  renderTimeline(assiduiteList, {
    date_debut: tlTime.deb,
    date_fin: tlTime.fin,
  });
}
/**
 * Ouverture du modal de conflit
 * @param {Array[Assiduité]} assiduiteList Liste des assiduités de l'étudiant
 */
function openModal(assiduiteList) {
  modal.style.display = "block";

  const tlTime = getTimeLineTimes();

  renderTimeline(assiduiteList, {
    date_debut: tlTime.deb,
    date_fin: tlTime.fin,
  });
}

/**
 * Fermeture du modal de conflit
 */
function closeModal() {
  modal.style.display = "none";
}

/**
 * Génération du modal
 * @param {Array[Assiduité]} assiduites la liste des assiduités à afficher
 * @param {Période} specialAssiduite Une assiduité représentant la période conflictuelle
 */
function renderTimeline(assiduites, specialAssiduite) {
  const timeLabels = document.querySelector(".time-labels");
  const assiduitesContainer = document.querySelector(".assiduites-container");

  timeLabels.innerHTML = "";
  assiduitesContainer.innerHTML = '<div class="assiduite-special"></div>';

  // Ajout des labels d'heure sur la frise chronologique
  // TODO permettre la modification des bornes (8 et 18)
  for (let i = 8; i <= 18; i++) {
    const timeLabel = document.createElement("div");
    timeLabel.className = "time-label";
    timeLabel.textContent = i < 10 ? `0${i}:00` : `${i}:00`;
    timeLabels.appendChild(timeLabel);
  }

  //Placement de la période conflictuelle sur la timeline
  const specialAssiduiteEl = document.querySelector(".assiduite-special");
  specialAssiduiteEl.style.width = getWidth(
    specialAssiduite.date_debut,
    specialAssiduite.date_fin
  );
  specialAssiduiteEl.style.left = getLeftPosition(specialAssiduite.date_debut);
  specialAssiduiteEl.style.top = "0";
  specialAssiduiteEl.style.zIndex = "0"; // Place l'assiduité spéciale en arrière-plan
  assiduitesContainer.appendChild(specialAssiduiteEl);
  const interval = {
    deb: new moment.tz(getDate(), TIMEZONE),
    fin: new moment.tz(getNextDate(), TIMEZONE),
  };
  //Placement des assiduités sur la timeline
  assiduites.forEach((assiduite) => {
    const period = {
      deb: new moment.tz(assiduite.date_debut, TIMEZONE),
      fin: new moment.tz(assiduite.date_fin, TIMEZONE),
    };
    if (!hasTimeConflict(period, interval)) {
      return;
    }
    const el = document.createElement("div");
    el.className = "assiduite";
    el.style.backgroundColor = getColor(assiduite.etat);
    el.style.width = getWidth(assiduite.date_debut, assiduite.date_fin);
    el.style.left = getLeftPosition(assiduite.date_debut);
    el.style.top = "10px";
    el.setAttribute("data-id", assiduite.assiduite_id);
    el.addEventListener("click", () => selectAssiduite(assiduite));

    // Ajout des informations dans la visualisation d'une assiduité
    const infoContainer = document.createElement("div");
    infoContainer.className = "assiduite-info";

    const idDiv = document.createElement("div");
    idDiv.className = "assiduite-id";
    idDiv.textContent = `ID: ${assiduite.assiduite_id}`;
    infoContainer.appendChild(idDiv);

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

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

    const userIdDiv = document.createElement("div");
    userIdDiv.className = "assiduite-user_id";
    userIdDiv.textContent = `saisi le ${formatDateModal(
      assiduite.entry_date,
      "à"
    )} \npar ${getUserFromId(assiduite.user_id)}`;
    infoContainer.appendChild(userIdDiv);

    el.appendChild(infoContainer);
    assiduitesContainer.appendChild(el);
  });
}

/**
 * Transformation d'une date de début en position sur la timeline
 * @param {String} start
 * @returns {String} un déplacement par rapport à la gauche en %
 */
function getLeftPosition(start) {
  const startTime = new moment.tz(start, TIMEZONE);
  const startMins = (startTime.hours() - 8) * 60 + startTime.minutes();
  return (startMins / (18 * 60 - 8 * 60)) * 100 + "%";
}

/**
 * Ajustement de l'espacement vertical entre les assiduités superposées
 * @param {HTMLElement} container le conteneur des assiduités
 * @param {String} start la date début de l'assiduité à placer
 * @param {String} end la date de fin de l'assiduité à placer
 * @returns {String} La position en px
 */
function getTopPosition(container, start, end) {
  const overlaps = (a, b) => {
    return a.start < b.end && a.end > b.start;
  };

  const startTime = new moment.tz(start, TIMEZONE);
  const endTime = new moment.tz(end, TIMEZONE);
  const assiduiteDuration = { start: startTime, end: endTime };

  let position = 0;
  let hasOverlap = true;

  while (hasOverlap) {
    hasOverlap = false;
    Array.from(container.children).some((el) => {
      const elStart = new moment.tz(el.getAttribute("data-start"));
      const elEnd = new moment.tz(el.getAttribute("data-end"));
      const elDuration = { start: elStart, end: elEnd };

      if (overlaps(assiduiteDuration, elDuration)) {
        position += 25; // Pour ajuster l'espacement vertical entre les assiduités superposées
        hasOverlap = true;
        return true;
      }
      return false;
    });
  }
  return position + "px";
}

/**
 * Transformation d'un état en couleur
 * @param {String} state l'état
 * @returns {String} la couleur correspondant à l'état
 */
function getColor(state) {
  switch (state) {
    case "PRESENT":
      return "#9CF1AF";
    case "ABSENT":
      return "#F1A69C";
    case "RETARD":
      return "#F1D99C";
    default:
      return "gray";
  }
}

/**
 * Calcule de la largeur de l'assiduité sur la timeline
 * @param {String} start date iso de début
 * @param {String} end date iso de fin
 * @returns {String} la taille en %
 */
function getWidth(start, end) {
  const startTime = new moment.tz(start, TIMEZONE);
  const endTime = new moment.tz(end, TIMEZONE);
  const duration = (endTime - startTime) / 1000 / 60;
  return (duration / (18 * 60 - 8 * 60)) * 100 + "%";
}

/**
 * Sélection d'une assiduité sur la timeline
 * @param {Assiduité} assiduite l'assiduité sélectionnée
 */
function selectAssiduite(assiduite) {
  // Désélectionner l'assiduité précédemment sélectionnée
  if (selectedAssiduite) {
    const prevSelectedEl = document.querySelector(
      `.assiduite[data-id="${selectedAssiduite.assiduite_id}"]`
    );
    if (prevSelectedEl) {
      prevSelectedEl.classList.remove("selected");
    }
  }

  // Sélectionner la nouvelle assiduité
  selectedAssiduite = assiduite;
  const selectedEl = document.querySelector(
    `.assiduite[data-id="${assiduite.assiduite_id}"]`
  );
  if (selectedEl) {
    selectedEl.classList.add("selected");
  }

  //Mise à jour de la partie information du modal
  const selectedModal = document.querySelector(".modal-assiduite-content");

  selectedModal.classList.add("show");

  document.getElementById("modal-assiduite-id").textContent =
    assiduite.assiduite_id;
  document.getElementById(
    "modal-assiduite-user"
  ).textContent = `saisi le ${formatDateModal(
    assiduite.entry_date,
    "à"
  )} \npar ${getUserFromId(assiduite.user_id)}`;
  document.getElementById("modal-assiduite-module").textContent =
    assiduite.moduleimpl_id;
  document.getElementById("modal-assiduite-deb").textContent = formatDateModal(
    assiduite.date_debut
  );
  document.getElementById("modal-assiduite-fin").textContent = formatDateModal(
    assiduite.date_fin
  );
  document.getElementById("modal-assiduite-etat").textContent =
    assiduite.etat.capitalize();

  //Activation des boutons d'actions de conflit
  deleteBtn.disabled = false;
  splitBtn.disabled = false;
  editBtn.disabled = false;
}
/**
 * Suppression de l'assiduité sélectionnée
 */
function deleteAssiduiteModal() {
  if (!selectedAssiduite) return;
  deleteAssiduite(selectedAssiduite.assiduite_id);
  actualizeEtud(selectedAssiduite.etudid);
  refreshModal(assiduites[selectedAssiduite.etudid]);

  // Désélection de l'assiduité
  resetSelection();
}

/**
 * Division d'une assiduité
 * @param {Assiduité} assiduite l'assiduité sélectionnée
 */
function splitAssiduiteModal(assiduite) {
  //Préparation du prompt
  const htmlPrompt = `<legend>Entrez l'heure de séparation (HH:mm) :</legend>
  <input type="time" id="promptTime" name="appt"
       min="08:00" max="18:00" required>`;

  const fieldSet = document.createElement("fieldset");
  fieldSet.classList.add("fieldsplit");
  fieldSet.innerHTML = htmlPrompt;

  //Callback de division
  const success = () => {
    const separatorTime = document.getElementById("promptTime").value;
    const dateString =
      document.querySelector("#tl_date").value + `T${separatorTime}`;
    const separtorDate = new moment.tz(dateString, TIMEZONE);

    const assiduite_debut = new moment.tz(assiduite.date_debut, TIMEZONE);
    const assiduite_fin = new moment.tz(assiduite.date_fin, TIMEZONE);

    if (
      separtorDate.isAfter(assiduite_debut) &&
      separtorDate.isBefore(assiduite_fin)
    ) {
      const assiduite_avant = {
        etat: assiduite.etat,
        date_debut: assiduite_debut.format(),
        date_fin: separtorDate.format(),
      };

      const assiduite_apres = {
        etat: assiduite.etat,
        date_debut: separtorDate.format(),
        date_fin: assiduite_fin.format(),
      };

      if (assiduite.moduleimpl_id) {
        assiduite_apres["moduleimpl_id"] = assiduite.moduleimpl_id;
        assiduite_avant["moduleimpl_id"] = assiduite.moduleimpl_id;
      }

      deleteAssiduite(assiduite.assiduite_id);

      const path = getUrl() + `/api/assiduite/${assiduite.etudid}/create`;
      sync_post(
        path,
        [assiduite_avant, assiduite_apres],
        (data, status) => {
          //success
        },
        (data, status) => {
          //error
          console.error(data, status);
        }
      );

      actualizeEtud(assiduite.etudid);
      refreshModal(assiduites[assiduite.etudid]);
      resetSelection();
    } else {
      const att = document.createTextNode(
        "L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée."
      );

      openAlertModal("Attention", att, "", "#ecb52a");
    }
  };

  openPromptModal("Entrée demandée", fieldSet, success, () => {}, "#37f05f");
}
/**
 * Modification d'une assiduité conflictuelle
 * @param {Assiduité} selectedAssiduite l'assiduité sélectionnée
 */
function editAssiduiteModal(selectedAssiduite) {
  if (!selectedAssiduite) return;

  //Préparation du modal d'édition
  const htmlPrompt = `<legend>Entrez l'état de l'assiduité :</legend>
  <select name="promptSelect" id="promptSelect" required>
    <option value="">Choissez l'état</option>
    <option value="present">Présent</option>
    <option value="retard">En Retard</option>
    <option value="absent">Absent</option>
</select>`;

  const fieldSet = document.createElement("fieldset");
  fieldSet.classList.add("fieldsplit");
  fieldSet.innerHTML = htmlPrompt;

  //Callback d'action d'édition
  const success = () => {
    const newState = document.getElementById("promptSelect").value;
    if (!["present", "absent", "retard"].includes(newState.toLowerCase())) {
      const att = document.createTextNode(
        "L'état doit être 'present', 'absent' ou 'retard'."
      );
      openAlertModal("Attention", att, "", "#ecb52a");
      return;
    }

    // Actualiser l'affichage

    editAssiduite(selectedAssiduite.assiduite_id, newState);
    actualizeEtud(selectedAssiduite.etudid);
    refreshModal(assiduites[selectedAssiduite.etudid]);

    // Désélection de l'assiduité
    resetSelection();
  };

  //Affichage du prompt
  openPromptModal("Entrée demandée", fieldSet, success, () => {}, "#37f05f");
}

/**
 * Remise à zéro de la sélection
 * Désactivation des boutons d'actions de conflit
 */
function resetSelection() {
  selectedAssiduite = null;
  deleteBtn.disabled = true;
  splitBtn.disabled = true;
  editBtn.disabled = true;

  document.querySelector(".modal-assiduite-content").classList.remove("show");
}
/**
 * Ajout des évents sur les boutons du modal
 */
window.onload = () => {
  modal = document.getElementById("myModal");
  closeBtn = document.querySelector(".close");
  timeline = document.getElementById("timeline");
  deleteBtn = document.getElementById("delete");
  splitBtn = document.getElementById("split");
  editBtn = document.getElementById("edit");
  selectedAssiduite = null;

  closeBtn?.addEventListener("click", closeModal);

  deleteBtn?.addEventListener("click", deleteAssiduiteModal);
  splitBtn?.addEventListener("click", () => {
    if (selectedAssiduite) {
      splitAssiduiteModal(selectedAssiduite);
    }
  });
  editBtn?.addEventListener("click", () => {
    if (selectedAssiduite) {
      editAssiduiteModal(selectedAssiduite);
    }
  });
};

// <<== Gestion de la récupération d'informations ==>>

/**
 * Récupération d'un nom d'utilisateur à partir d'un identifiant
 * @param {Number} id identifiant de l'utilisateur
 * @returns {String} le nom de l'utilisateur ou son pseudo ou "Non Renseigné"
 */
function getUserFromId(id) {
  if (id == "") {
    return "Non Renseigné";
  }

  let name = "Non Renseigné";

  sync_get(`/ScoDoc/api/user/${id}`, (data) => {
    if (data.nom != "" && data.prenom != "") {
      name = `${data.nom} ${data.prenom}`;
    } else {
      name = data.user_name;
    }
  });

  return name;
}

/**
 * Récupération des ids des groupes
 * @returns la liste des ids des groupes
 */
function getGroupIds() {
  const btns = document.querySelector(".multiselect-container.dropdown-menu");

  const groups = Array.from(btns.querySelectorAll(".active")).map((el) => {
    return el.querySelector("input").value;
  });

  return groups;
}

/**
 * Récupération du moduleimpl_id
 * @returns {String} l'identifiant ou null si inéxistant
 */
function getModuleImplId() {
  const val = document.querySelector("#moduleimpl_select")?.value;
  return ["", undefined, null].includes(val) ? null : val;
}

/**
 * Récupération de l'id du formsemestre
 * @returns {String} l'identifiant du formsemestre
 */
function getFormSemestreId() {
  return document.querySelector(".formsemestre_id").textContent;
}

/**
 * Récupère la période du semestre
 * @returns {object} période {deb,fin}
 */
function getFormSemestreDates() {
  const dateDeb = document.getElementById(
    "formsemestre_date_debut"
  ).textContent;
  const dateFin = document.getElementById("formsemestre_date_fin").textContent;

  return {
    deb: dateDeb,
    fin: dateFin,
  };
}

/**
 * Récupère un objet étudiant à partir de son id
 * @param {Number} etudid
 */
function getSingleEtud(etudid) {
  sync_get(getUrl() + `/api/etudiant/etudid/${etudid}`, (data) => {
    etuds[etudid] = data;
  });
}

function isSingleEtud() {
  return location.href.includes("SignaleAssiduiteEtud");
}

function getCurrentAssiduiteModuleImplId() {
  const currentAssiduites = getAssiduitesConflict(etudid);
  if (currentAssiduites.length > 0) {
    const mod = currentAssiduites[0].moduleimpl_id;
    return mod == null ? "" : mod;
  }
  return "";
}

function getCurrentAssiduite(etudid) {
  const field = document.querySelector(
    `fieldset.btns_field.single[etudid='${etudid}']`
  );

  if (!field) return null;

  const assiduite_id = parseInt(field.getAttribute("assiduite_id"));
  const type = field.getAttribute("type");

  if (type == "édition") {
    let assi = null;
    assiduites[etudid].forEach((a) => {
      if (a.assiduite_id === assiduite_id) {
        assi = a;
      }
    });
    return assi;
  } else {
    return null;
  }
}

// <<== Gestion de la justification ==>>

function getJustificatifFromPeriod(date, etudid, update) {
  $.ajax({
    async: true,
    type: "GET",
    url:
      getUrl() +
      `/api/justificatifs/${etudid}/query?date_debut=${date.deb
        .add(1, "s")
        .format()}&date_fin=${date.fin.subtract(1, "s").format()}`,
    success: (data) => {
      update(data);
    },
    error: () => {},
  });
}

function updateJustifyBtn() {
  if (isSingleEtud()) {
    const assi = getCurrentAssiduite(etudid);

    const just = assi ? !assi.est_just : false;
    const btn = document.getElementById("justif-rapide");
    if (!just) {
      btn.setAttribute("disabled", "true");
    } else {
      btn.removeAttribute("disabled");
    }
  }
}

function fastJustify(assiduite) {
  const period = {
    deb: new moment.tz(assiduite.date_debut, TIMEZONE),
    fin: new moment.tz(assiduite.date_fin, TIMEZONE),
  };
  const action = (justifs) => {
    if (justifs.length > 0) {
      justifyAssiduite(assiduite.assiduite_id, !assiduite.est_just);
    } else {
      console.debug("WIP");
      //créer un nouveau justificatif
      // Afficher prompt -> demander raison et état

      const success = () => {
        const raison = document.getElementById("promptText").value;
        const etat = document.getElementById("promptSelect").value;

        //créer justificatif

        const justif = {
          date_debut: new moment.tz(assiduite.date_debut, TIMEZONE)
            .add(1, "s")
            .format(),
          date_fin: new moment.tz(assiduite.date_fin, TIMEZONE)
            .subtract(1, "s")
            .format(),
          raison: raison,
          etat: etat,
        };

        createJustificatif(justif);

        // justifyAssiduite(assiduite.assiduite_id, true);
        generateAllEtudRow();
      };

      const content = document.createElement("fieldset");

      const htmlPrompt = `<legend>Entrez l'état du justificatif :</legend>
    <select name="promptSelect" id="promptSelect" required>
      <option value="valide">Valide</option>
      <option value="attente">En Attente de validation</option>
      <option value="non_valide">Non Valide</option>
      <option value="modifie">Modifié</option>
  </select>
  <legend>Raison:</legend>
  <textarea type="text" placeholder="Explication du justificatif (non obligatoire)" id="promptText" style="width:100%;"></textarea>
  `;

      content.innerHTML = htmlPrompt;

      openPromptModal(
        "Nouveau justificatif (Rapide)",
        content,
        success,
        () => {},
        "#7059FF"
      );
    }
  };
  if (assiduite.etudid) {
    getJustificatifFromPeriod(period, assiduite.etudid, action);
  }
}

function justifyAssiduite(assiduite_id, justified) {
  const assiduite = {
    est_just: justified,
  };
  const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`;
  let bool = false;
  sync_post(
    path,
    assiduite,
    (data, status) => {
      bool = true;
    },
    (data, status) => {
      //error
      console.error(data, status);
    }
  );

  return bool;
}

function createJustificatif(justif) {
  const path = getUrl() + `/api/justificatif/${etudid}/create`;
  sync_post(
    path,
    [justif],
    (data, status) => {
      //success
      if (data.success.length > 0) {
        console.table(data[0]);
      }
      console.warn(data);
    },
    (data, status) => {
      //error
      console.error(data, status);
    }
  );
}

function getAllJustificatifsFromEtud(etudid, action) {
  const url_api = getUrl() + `/api/justificatifs/${etudid}`;
  $.ajax({
    async: true,
    type: "GET",
    url: url_api,
    success: (data, status) => {
      if (status === "success") {
        action(data);
      }
    },
    error: () => {},
  });
}

function deleteJustificatif(justif_id) {
  const path = getUrl() + `/api/justificatif/delete`;
  sync_post(
    path,
    [justif_id],
    (data, status) => {
      //success
      if (data.success.length > 0) {
      }
    },
    (data, status) => {
      //error
      console.error(data, status);
    }
  );
}
