Source: modal.js

/**
 * Manages modal display logic for content details and video playback.
 *
 * This module handles the opening and dynamic rendering of modals for both films and series,
 * including displaying metadata, trailers, IMDb links, cast, and watched status. It also manages
 * playback of videos in a dedicated modal with resume support and watched progress tracking.
 * Series episodes are displayed per season with interactive controls, and the module keeps track
 * of the user's playback context to update localStorage as needed.
 *
 * @module modal
 */

let currentVideoContext = null;
let lastOpenedContent = null;

/**
 * Shows the film detail modal for a given film.
 *
 * @param film - The film object containing metadata like title, description, genres, etc.
 */
function showFilmModal(film) {
    if (film) openModal(film.title, "film");
}

/**
 * Shows the series detail modal for a given series.
 *
 * @param series - The series object containing metadata like title, description, seasons, etc.
 */
function showSeriesModal(series) {
    if (series) openModal(series.title, "series");
}

/**
 * Opens the content detail modal for a given film or series.
 *
 * Renders the modal content using the provided title and type, and sets up additional UI
 * for series (like seasons and episodes). Prevents background scrolling while the modal is open.
 *
 * @function
 * @param {string} title - The title of the content item.
 * @param {string} type - The content type: "film" or "series".
 */
function openModal(title, type) {
    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);
    }
    currentVideoContext = null;
}

/**
 * Generates the HTML content of the modal based on the item’s metadata.
 *
 * Builds a visual layout with poster, title, rating, genres, year, description,
 * cast, and available actions like watch/trailer/IMDb buttons.
 *
 * @function
 * @param {Object} item - The content item (film or series).
 * @param {string} type - The content type: "film" or "series".
 * @returns {string} HTML string to be injected into the modal.
 */
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 UI for a series, including seasons navigation and episodes display.
 *
 * Builds buttons for each season and loads the episodes for the first one by default.
 *
 * @function
 * @param {Object} series - The series object with seasons and episodes.
 */
function setupSeriesModal(series) {
    if (!series.seasons) return;

    const seasonsContainer = document.getElementById("seasonsContainer");
    const seasons = Object.keys(series.seasons);

    seasonsContainer.innerHTML = `
    <div class="seasons-section">
      <h3>Saisons et Épisodes</h3>
      <div class="seasons-nav">
        ${seasons
        .map((season, index) => `<button class="season-btn ${index === 0 ? "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, seasons[0]);
}

/**
 * Displays the list of episodes for a given season in the modal.
 *
 * Generates episode cards with titles, descriptions, watched status,
 * and a click handler to start playback.
 *
 * @function
 * @param {Object} series - The series object.
 * @param {string} seasonNumber - The selected season number to display.
 */
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("");
}

/**
 * Starts video playback in the video modal and initializes playback context.
 *
 * Sets the video source, handles resume playback from last watched time,
 * and updates `currentVideoContext` for tracking progress.
 *
 * @function
 * @param {string} videoPath - The path to the video file.
 * @param {string} type - "film" or "series".
 * @param {string} title - Title of the content.
 * @param {string} [season] - Season number (only for series).
 * @param {number} [epIndex] - Episode index (only for series).
 */
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();
}

/**
 * Updates watch progress as the video plays.
 *
 * Saves the current time to localStorage and marks the item as fully watched
 * if more than 90% of the video is completed.
 *
 * @function
 */
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);
        }
    }
}

/**
 * Marks the content as fully watched when the video ends.
 *
 * Called automatically when video playback reaches the end.
 *
 * @function
 */
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);
    }
}

/**
 * Saves the current playback position when the video is paused.
 *
 * Updates watch progress in localStorage without marking as fully watched.
 *
 * @function
 */
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 open modals and resets video playback state.
 *
 * Stops the video, clears playback context, and re-enables page scrolling.
 *
 * @function
 */
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));
        }
    }
}