Source: modal.js

/**
 * @module modal
 * @description
 * Controls the display and behavior of modal dialogs (pop-ups) in the UI.
 * Handles opening, closing, and content management for modals.
 */

let currentVideoContext = null;
let lastOpenedContent = null;

/**
 * Displays the modal for a film with its details.
 *
 * @function
 * @param {Object} film - The film object to display.
 * @returns {void}
 */
function showFilmModal(film) {
  if (film) openModal(film.title, "film", 0);
}

/**
 * Displays the modal for a series with its details and selected season.
 *
 * @function
 * @param {Object} series - The series object to display.
 * @param {string|number} season - The season to display.
 * @returns {void}
 */
function showSeriesModal(series, season) {
  if (series) openModal(series.title, "series", season);
}

/**
 * Opens the modal for a given content item (film or series).
 *
 * @function
 * @param {string} title - The title of the content.
 * @param {string} type - The type of content ("film" or "series").
 * @param {string|number} season - The season to display (for series).
 * @returns {void}
 */
function openModal(title, type, season) {
  const item = type === "film" ? filmsData[title] : seriesData[title];
  if (!item) return;

  const modalBody = document.getElementById("modalBody");
  modalBody.innerHTML = createModalContent(item, type);
  modal.classList.add("active");
  document.body.style.overflow = "hidden";

  if (type === "series" && item.seasons) {
    setupSeriesModal(item, season);
  }
  currentVideoContext = null;
}

/**
 * Generates the HTML content for the modal based on the item and its type.
 *
 * @function
 * @param {Object} item - The content item (film or series).
 * @param {string} type - The type of content ("film" or "series").
 * @returns {string} The HTML string for the modal content.
 */
function createModalContent(item, type) {
  const genres = item.genres
    ? item.genres.map((g) => `<span class="genre-tag">${g}</span>`).join("")
    : "";
  const rating = item.IMDb
    ? `<div class="modal-rating"><i class="fas fa-star"></i> ${item.IMDb}/10</div>`
    : "";
  const year = item.year ? `<span class="year-tag">${item.year}</span>` : "";
  const directors = item.directors
    ? `<p><strong>Réalisateurs:</strong> ${item.directors.join(", ")}</p>`
    : "";
  const writers = item.writers
    ? `<p><strong>Scénaristes:</strong> ${item.writers.join(", ")}</p>`
    : "";
  const stars = item.stars
    ? `<p><strong>Acteurs:</strong> ${item.stars.join(", ")}</p>`
    : "";
  const creators = item.creators
    ? `<p><strong>Créateurs:</strong> ${item.creators.join(", ")}</p>`
    : "";

  let watchedInfo = "";
  if (type === "film") {
    const watchData = getFilmWatchData(item.title);
    if (watchData.watched) {
      watchedInfo = `<span class="watched-badge" title="Déjà vu"><i class="fas fa-eye"></i> Vu</span>`;
    }
  }

  const watchButton =
    type === "film" && item.video
      ? `<button class="btn btn-primary" onclick="playVideo('${
          item.video
        }', 'film', '${escapeForHTML(item.title)}')">
      <i class="fas fa-play"></i> Regarder
    </button>`
      : "";

  const trailerButton = item.trailer
    ? `<a href="${item.trailer}" target="_blank" class="btn btn-secondary">
      <i class="fas fa-external-link-alt"></i> Bande-annonce
    </a>`
    : "";

  const imdbButton = item.IMDb_link
    ? `<a href="${item.IMDb_link}" target="_blank" class="btn btn-secondary">
      <i class="fas fa-external-link-alt"></i> IMDb
    </a>`
    : "";

  return `
    <div class="modal-body">
      <div class="modal-header">
        <div class="modal-poster">
          ${
            item.banner
              ? `<img src="${item.banner}" alt="${item.title}" class="modal-poster">`
              : '<div class="modal-poster" style="display: flex; align-items: center; justify-content: center; background-color: var(--bg-card);"><i class="fas fa-film" style="font-size: 3rem; color: var(--text-secondary);"></i></div>'
          }
        </div>
        <div class="modal-info">
          <h2 class="modal-title">${item.title} ${watchedInfo}</h2>
          <div class="modal-meta">
            ${rating}
            ${year}
            <div class="card-genres">${genres}</div>
          </div>
          <p class="modal-description">${
            item.description || "Aucune description disponible."
          }</p>
          <div class="modal-cast">
            ${directors}
            ${creators}
            ${writers}
            ${stars}
          </div>
          <div class="action-buttons">
            ${watchButton}
            ${trailerButton}
            ${imdbButton}
          </div>
        </div>
      </div>
      ${type === "series" ? '<div id="seasonsContainer"></div>' : ""}
    </div>
  `;
}

/**
 * Sets up the modal for a series, displaying seasons and episodes.
 *
 * @function
 * @param {Object} series - The series object.
 * @param {string|number} defaultSeason - The season to display initially.
 * @returns {void}
 */
function setupSeriesModal(series, defaultSeason) {
  if (!series.seasons) return;

  const seasonsContainer = document.getElementById("seasonsContainer");
  const seasons = Object.keys(series.seasons);
  const initialSeason =
    defaultSeason && seasons.includes(defaultSeason)
      ? defaultSeason
      : seasons[0];

  seasonsContainer.innerHTML = `
    <div class="seasons-section">
      <h3>Saisons et Épisodes</h3>
      <div class="seasons-nav">
        ${seasons
          .map(
            (season) => `<button class="season-btn ${
              season === initialSeason ? "active" : ""
            }" data-season="${season}">
            Saison ${season}
          </button>`
          )
          .join("")}
      </div>
      <div id="episodesContainer"></div>
    </div>
  `;

  const seasonBtns = seasonsContainer.querySelectorAll(".season-btn");
  seasonBtns.forEach((btn) => {
    btn.addEventListener("click", () => {
      seasonBtns.forEach((b) => b.classList.remove("active"));
      btn.classList.add("active");
      displayEpisodes(series, btn.dataset.season);
    });
  });

  displayEpisodes(series, initialSeason);
}

/**
 * Displays the episodes for a given series and season in the modal.
 *
 * @function
 * @param {Object} series - The series object.
 * @param {string|number} seasonNumber - The season number to display.
 * @returns {void}
 */
function displayEpisodes(series, seasonNumber) {
  const episodes = series.seasons[seasonNumber];
  const episodesContainer = document.getElementById("episodesContainer");

  if (!episodes || episodes.length === 0) {
    episodesContainer.innerHTML =
      "<p>Aucun épisode disponible pour cette saison.</p>";
    return;
  }

  episodesContainer.innerHTML = episodes
    .map((episode, index) => {
      const watchData = getEpisodeWatchData(series.title, seasonNumber, index);
      const watchedClass = watchData.watched ? "watched-episode" : "";
      const watchedBadge = watchData.watched
        ? `<span class="watched-badge" title="Déjà vu"><i class="fas fa-eye"></i></span>`
        : "";
      return `
        <div class="episode-item ${watchedClass}" onclick="playVideo('${
        episode.video
      }', 'series', '${escapeForHTML(
        series.title
      )}', '${seasonNumber}', ${index})">
          <div class="episode-number">E${index + 1}</div>
          <div class="episode-info">
            <div class="episode-title">${episode.title} ${watchedBadge}</div>
            <div class="episode-description">${episode.desc}</div>
          </div>
          <div style="display: flex; align-items: center;">
            <i class="fas fa-play" style="color: var(--accent);"></i>
          </div>
        </div>
      `;
    })
    .join("");
}

/**
 * Plays a video (film or episode) in the video modal, resuming from last watched time if available.
 *
 * @function
 * @param {string} videoPath - The path to the video file.
 * @param {string} type - The type of content ("film" or "series").
 * @param {string} title - The title of the content.
 * @param {string|number} [season] - The season number (for series).
 * @param {number} [epIndex] - The episode index (for series).
 * @returns {void}
 */
function playVideo(videoPath, type, title, season, epIndex) {
  if (!videoPath) {
    alert("Vidéo non disponible");
    return;
  }

  lastOpenedContent = { type, title, season, epIndex };

  videoPlayer.src = videoPath;
  videoModal.classList.add("active");
  modal.classList.remove("active");
  document.body.style.overflow = "hidden";

  let startTime = 0;
  if (type === "film") {
    const watchData = getFilmWatchData(title);
    startTime = watchData.time || 0;
    currentVideoContext = { type, title };
  } else if (type === "series") {
    const watchData = getEpisodeWatchData(title, season, epIndex);
    startTime = watchData.time || 0;
    currentVideoContext = { type, title, season, epIndex };
  }
  videoPlayer.currentTime = startTime;
  setTimeout(() => {
    videoPlayer.currentTime = startTime;
  }, 100);

  videoPlayer.play();
}

/**
 * Handles updating watch progress while the video is playing.
 *
 * @function
 * @returns {void}
 */
function handleVideoTimeUpdate() {
  if (!currentVideoContext) return;
  const { type, title, season, epIndex } = currentVideoContext;
  const duration = videoPlayer.duration || 1;
  const current = videoPlayer.currentTime;

  if (type === "film") {
    markFilmWatched(title, false, current);
  } else if (type === "series") {
    markEpisodeWatched(title, season, epIndex, false, current);
  }

  const remaining = duration - current;
  if (remaining <= 180) {
    if (type === "film") {
      markFilmWatched(title, true, 0);
    } else if (type === "series") {
      markEpisodeWatched(title, season, epIndex, true, 0);
    }
  }
}

/**
 * Handles marking content as watched when the video ends.
 *
 * @function
 * @returns {void}
 */
function handleVideoEnded() {
  if (!currentVideoContext) return;
  const { type, title, season, epIndex } = currentVideoContext;
  if (type === "film") {
    markFilmWatched(title, true, 0);
  } else if (type === "series") {
    markEpisodeWatched(title, season, epIndex, true, 0);
  }
}

/**
 * Handles saving watch progress when the video is paused.
 *
 * @function
 * @returns {void}
 */
function handleVideoPause() {
  if (!currentVideoContext) return;
  const { type, title, season, epIndex } = currentVideoContext;
  const current = videoPlayer.currentTime;
  if (type === "film") {
    markFilmWatched(title, false, current);
  } else if (type === "series") {
    markEpisodeWatched(title, season, epIndex, false, current);
  }
}

/**
 * Closes all modals and restores the previous state.
 *
 * @function
 * @returns {void}
 */
function closeModals() {
  const wasVideoModalActive = videoModal.classList.contains("active");
  modal.classList.remove("active");
  videoModal.classList.remove("active");
  document.body.style.overflow = "auto";

  if (videoPlayer) {
    videoPlayer.pause();
    videoPlayer.src = "";
  }
  currentVideoContext = null;

  if (wasVideoModalActive && lastOpenedContent) {
    if (lastOpenedContent.type === "film") {
      showFilmModal(getFilmByTitle(lastOpenedContent.title));
    } else if (lastOpenedContent.type === "series") {
      showSeriesModal(
        getSeriesByTitle(lastOpenedContent.title),
        lastOpenedContent.season
      );
    }
  }
}