Source: display.js

/**
 * @module display
 * @description
 * Manages rendering and updating the UI for films, series, and their details.
 * Handles DOM manipulation for displaying media content and metadata.
 */

/**
 * Displays the most popular films and series based on IMDb ratings.
 *
 * Sorts and selects the top 6 films and series, then renders them in their respective containers.
 *
 * @function
 * @returns {void}
 */
function displayPopularContent() {
  const popularFilms = Object.values(filmsData)
    .sort((a, b) => (b.IMDb || 0) - (a.IMDb || 0))
    .slice(0, 6);

  const popularSeries = Object.values(seriesData)
    .sort((a, b) => (b.IMDb || 0) - (a.IMDb || 0))
    .slice(0, 6);

  displayContent(popularFilms, "popularFilms");
  displayContent(popularSeries, "popularSeries");
}

/**
 * Displays a sorted list of films in the films grid container.
 *
 * @function
 * @param {Object[]} films - Array of film objects to display.
 * @returns {void}
 */
function displayFilms(films) {
  const sortedFilms = films
    .slice()
    .sort((a, b) => a.title.localeCompare(b.title));
  displayContent(sortedFilms, "filmsGrid");
}

/**
 * Displays a sorted list of series in the series grid container.
 *
 * @function
 * @param {Object[]} series - Array of series objects to display.
 * @returns {void}
 */
function displaySeries(series) {
  const sortedSeries = series
    .slice()
    .sort((a, b) => a.title.localeCompare(b.title));
  displayContent(sortedSeries, "seriesGrid");
}

/**
 * Renders a list of content items (films or series) into a specified container.
 *
 * @function
 * @param {Object[]} items - Array of content items to display.
 * @param {string} containerId - The DOM element ID where content will be injected.
 * @returns {void}
 */
function displayContent(items, containerId) {
  const container = document.getElementById(containerId);
  if (!container) return;

  if (!items || items.length === 0) {
    container.innerHTML =
      '<div class="no-results"><i class="fas fa-film"></i><p>Aucun contenu trouvé</p></div>';
    return;
  }

  container.innerHTML = items.map((item) => createContentCard(item)).join("");
}

/**
 * Generates the HTML for a single content card (film or series).
 *
 * @function
 * @param {Object} item - The content item to render.
 * @returns {string} The HTML string for the content card.
 */
function createContentCard(item) {
  const genres = item.genres
    ? item.genres
        .slice(0, 3)
        .map((g) => `<span class="genre-tag">${g}</span>`)
        .join("")
    : "";
  const rating = item.IMDb
    ? `<div class="card-rating"><i class="fas fa-star"></i> ${item.IMDb}</div>`
    : "";
  const year = item.year ? `<span class="card-year">${item.year}</span>` : "";

  let watchedBadge = "";
  if (getItemType(item) === "film") {
    const watchData = getFilmWatchData(item.title);
    if (watchData.watched) {
      watchedBadge = `<span class="watched-badge" title="Déjà vu"><i class="fas fa-eye"></i></span>`;
    }
  } else if (getItemType(item) === "series") {
    if (isSeriesFullyWatched(item)) {
      watchedBadge = `<span class="watched-badge" title="Série vue en entier"><i class="fas fa-eye"></i></span>`;
    }
  }

  return `
      <div class="content-card" onclick="openModal('${escapeForHTML(
        item.title
      )}', '${getItemType(item)}')">
        <div class="card-image">
          ${
            item.banner
              ? `<img src="${item.banner}" alt="${item.title}" onerror="this.style.display='none'">`
              : '<i class="fas fa-film"></i>'
          }
          ${watchedBadge}
        </div>
        <div class="card-content">
          <h3 class="card-title">${item.title}</h3>
          <div class="card-meta">
            ${rating}
            ${year}
          </div>
          <div class="card-genres">${genres}</div>
        </div>
      </div>
    `;
}

/**
 * Determines the type of a content item (film or series).
 *
 * @function
 * @param {Object} item - The content item to check.
 * @returns {string} Returns `"series"` if the item has a `seasons` property, otherwise `"film"`.
 */
function getItemType(item) {
  return item.seasons ? "series" : "film";
}

/**
 * Generates the HTML for a "no results" message.
 *
 * @function
 * @param {string} [message="Aucun résultat trouvé"] - The message to display.
 * @returns {string} The HTML string for the no results message.
 */
function showNoResults(message = "Aucun résultat trouvé") {
  return `
    <div class="no-results">
      <i class="fas fa-search"></i>
      <p>${message}</p>
    </div>
  `;
}

/**
 * Retrieves the most recently added film from the films data.
 *
 * @function
 * @returns {Object} The latest film object.
 */
function getLatestFilm() {
  const films = Object.values(filmsData);
  return films[films.length - 1];
}

/**
 * Retrieves the most recently added series from the series data.
 *
 * @function
 * @returns {Object} The latest series object.
 */
function getLatestSeries() {
  const series = Object.values(seriesData);
  return series[series.length - 1];
}

/**
 * Renders the featured slider with the latest film and series.
 *
 * Handles slider navigation, auto-sliding, and click events to open modals for featured items.
 *
 * @function
 * @returns {void}
 */
function renderFeaturedSlider() {
  const film = getLatestFilm();
  const series = getLatestSeries();
  const slider = document.getElementById("featuredSlider");
  if (!slider) return;

  const featuredItems = [film, series];
  let currentIndex = 0;
  let autoSlideInterval;

  function render() {
    slider.innerHTML = `
      <div class="slider-wrapper">
        <div class="slider-track">
          ${featuredItems
            .map(
              (item, idx) => `
              <div class="slider-card featured ${
                idx === currentIndex ? "active" : ""
              }"
                   data-idx="${idx}" style="cursor:pointer;" aria-hidden="${
                idx === currentIndex ? "false" : "true"
              }">
                <img src="${item.banner}" alt="${
                item.title
              }" class="slider-img" onerror="this.style.display='none'">
                <div class="slider-overlay">
                  <div class="slider-card-title">${item.title}</div>
                  <div class="slider-card-desc">${item.description}</div>
                </div>
              </div>
            `
            )
            .join("")}
        </div>
        <div class="slider-dots">
          ${featuredItems
            .map(
              (_, dotIdx) => `
                <span class="slider-dot${
                  dotIdx === currentIndex ? " active" : ""
                }" data-idx="${dotIdx}"></span>
              `
            )
            .join("")}
        </div>
      </div>
    `;

    slider.querySelectorAll(".slider-card.featured").forEach((card) => {
      card.onclick = () => {
        const idx = parseInt(card.getAttribute("data-idx"));
        const item = featuredItems[idx];
        openModal(item.title, item.seasons ? "series" : "film");
      };
    });

    slider.querySelectorAll(".slider-dot").forEach((dot) => {
      dot.onclick = (e) => {
        e.stopPropagation();
        currentIndex = parseInt(dot.getAttribute("data-idx"));
        render();
        resetAutoSlide();
      };
    });
  }

  function nextSlide() {
    currentIndex = (currentIndex + 1) % featuredItems.length;
    render();
  }

  function resetAutoSlide() {
    clearInterval(autoSlideInterval);
    autoSlideInterval = setInterval(nextSlide, 5000);
  }

  render();
  resetAutoSlide();
}

/**
 * Displays available collections in the collections grid.
 * Fetches `data/collections.json` if `collectionsData` is not already loaded.
 *
 * @function
 * @returns {Promise<void>}
 */
async function displayCollections() {
  const container = document.getElementById("collectionsGrid");
  if (!container) return;

  try {
    if (typeof collectionsData === "undefined") {
      const resp = await fetch("data/collections.json");
      collectionsData = await resp.json();
    }

    const collections = Object.values(collectionsData || {}).sort((a, b) =>
      a.name.localeCompare(b.name, "fr", { sensitivity: "base" })
    );
    if (!collections || collections.length === 0) {
      container.innerHTML =
        '<div class="no-results"><i class="fas fa-folder-open"></i><p>Aucune collection trouvée</p></div>';
      return;
    }

    container.innerHTML = collections
      .map((col) => createCollectionCard(col))
      .join("");
  } catch (err) {
    console.error("Erreur en chargeant les collections:", err);
    container.innerHTML =
      '<div class="no-results"><i class="fas fa-exclamation-triangle"></i><p>Erreur lors du chargement des collections</p></div>';
  }
}

/**
 * Creates HTML for a single collection card.
 * Clicking the card opens a modal that lists all films and series in the collection.
 *
 * @function
 * @param {Object} collection - The collection object ({ name, films, series }).
 * @returns {string} HTML string for the collection card.
 */
function createCollectionCard(collection) {
  const filmsCount = collection.films ? collection.films.length : 0;
  const seriesCount = collection.series ? collection.series.length : 0;

  let thumbnail = "";
  if (collection.films && collection.films.length > 0) {
    const first = filmsData[collection.films[0]];
    if (first && first.banner) thumbnail = first.banner;
  }
  if (!thumbnail && collection.series && collection.series.length > 0) {
    const firstS = seriesData[collection.series[0]];
    if (firstS && firstS.banner) thumbnail = firstS.banner;
  }

  const thumbHtml = thumbnail
    ? `<img src="${thumbnail}" alt="${collection.name}" onerror="this.style.display='none'">`
    : '<i class="fas fa-folder-open"></i>';

  return `
    <div class="content-card" onclick="showCollectionPage('${escapeForHTML(
      collection.name
    )}')">
      <div class="card-image">
        ${thumbHtml}
      </div>
      <div class="card-content">
        <h3 class="card-title">${collection.name}</h3>
        <div class="card-meta">
          <span class="card-year">${filmsCount} films • ${seriesCount} séries</span>
        </div>
      </div>
    </div>
  `;
}

/**
 * Navigates to the collection page and displays its content.
 *
 * @function
 * @param {string} collectionName
 */
function showCollectionPage(collectionName) {
  if (!collectionName) return;
  (async () => {
    try {
      if (typeof collectionsData === "undefined") {
        const resp = await fetch("data/collections.json");
        collectionsData = await resp.json();
      }
      const collection = collectionsData[collectionName];
      if (!collection) {
        alert("Collection introuvable");
        return;
      }
      displayCollectionPage(collection);
      showSection("collection");
    } catch (err) {
      console.error("Erreur en ouvrant la collection:", err);
      alert("Erreur lors de l'ouverture de la collection");
    }
  })();
}

/**
 * Renders the collection page with films and series grids.
 * @param {Object} collection
 */
function displayCollectionPage(collection) {
  const titleEl = document.getElementById("collectionTitle");
  const filmsGrid = document.getElementById("collectionFilmsGrid");
  const seriesGrid = document.getElementById("collectionSeriesGrid");
  if (!titleEl || !filmsGrid || !seriesGrid) return;

  titleEl.textContent = `${collection.name}`;

  const films = (collection.films || [])
    .map((t) => filmsData[t])
    .filter(Boolean);
  const series = (collection.series || [])
    .map((t) => seriesData[t])
    .filter(Boolean);

  const filmsSection = filmsGrid.closest(".content-section");
  if (films.length) {
    filmsGrid.innerHTML = films.map((f) => createContentCard(f)).join("");
    if (filmsSection) filmsSection.style.display = "block";
  } else {
    filmsGrid.innerHTML = "";
    if (filmsSection) filmsSection.style.display = "none";
  }

  const seriesSection = seriesGrid.closest(".content-section");
  if (series.length) {
    seriesGrid.innerHTML = series.map((s) => createContentCard(s)).join("");
    if (seriesSection) seriesSection.style.display = "block";
  } else {
    seriesGrid.innerHTML = "";
    if (seriesSection) seriesSection.style.display = "none";
  }
}