// ============================================================
// SECTION — CONSEILS ACTIONNABLES (vidéos verticales 9:16)
// ============================================================

// POUR AJOUTER UNE VIDÉO :
//   1. compresser le .mp4 en 720x1280 et le déposer dans assets/videos/
//   2. déposer le poster .jpg du même nom à côté
//   3. copier un objet ci-dessous et remplir les champs
// Les filtres par thème et le compteur se déduisent automatiquement
// de ce tableau. Rien d'autre à modifier.
const VIDEOS = [
  {
    id: "kpi-ia-piloter-son-club",
    title: "Piloter son club avec l'IA",
    pitch: "Les KPI à suivre vraiment, et comment l'IA vous les sort en quelques secondes.",
    theme: "Pilotage",
    duration: "1:05",
    src: "assets/videos/01-kpi-ia-piloter-son-club.mp4",
    poster: "assets/videos/01-kpi-ia-piloter-son-club.jpg",
  },
  {
    id: "formation-continue-commerciaux",
    title: "Fidéliser par la formation",
    pitch: "La formation continue est votre meilleur levier de rétention d'équipe.",
    theme: "Management",
    duration: "0:57",
    src: "assets/videos/03-formation-continue-commerciaux.mp4",
    poster: "assets/videos/03-formation-continue-commerciaux.jpg",
  },
  {
    id: "remplissage-crm-data",
    title: "Un CRM rempli, une data fiable",
    pitch: "Sans discipline de saisie, vos chiffres ne valent rien. Comment installer le réflexe.",
    theme: "Pilotage",
    duration: "1:05",
    src: "assets/videos/04-remplissage-crm-data.mp4",
    poster: "assets/videos/04-remplissage-crm-data.jpg",
  },
  {
    id: "gamification-objectifs",
    title: "Gamifier vos objectifs",
    pitch: "Transformer le chiffre à atteindre en jeu d'équipe plutôt qu'en pression.",
    theme: "Commercial",
    duration: "1:06",
    src: "assets/videos/05-gamification-objectifs.mp4",
    poster: "assets/videos/05-gamification-objectifs.jpg",
  },
  {
    id: "impliquer-les-coachs-3x3",
    title: "Impliquer vos coachs : le 3x3",
    pitch: "Trois actions, trois échéances, pour embarquer l'équipe terrain.",
    theme: "Management",
    duration: "1:00",
    src: "assets/videos/06-impliquer-les-coachs-3x3.mp4",
    poster: "assets/videos/06-impliquer-les-coachs-3x3.jpg",
  },
  {
    id: "video-coach-veille-showup",
    title: "+20 % de show-up en 1 vidéo",
    pitch: "Un message vidéo du coach envoyé la veille change le taux de présence.",
    theme: "Expérience client",
    duration: "1:20",
    src: "assets/videos/07-video-coach-veille-showup.mp4",
    poster: "assets/videos/07-video-coach-veille-showup.jpg",
  },
  {
    id: "micro-management-5-minutes",
    title: "Micro-management en 5 min",
    pitch: "Le point quotidien ultra-court qui remplace les réunions interminables.",
    theme: "Management",
    duration: "1:41",
    src: "assets/videos/08-micro-management-5-minutes.mp4",
    poster: "assets/videos/08-micro-management-5-minutes.jpg",
  },
  {
    id: "deleguer-reveler-potentiels",
    title: "Déléguer révèle les talents",
    pitch: "La délégation individualisée comme outil de détection des talents.",
    theme: "Management",
    duration: "1:51",
    src: "assets/videos/09-deleguer-reveler-potentiels.mp4",
    poster: "assets/videos/09-deleguer-reveler-potentiels.jpg",
  },
];

// ---- Lecture automatique fiable sur iPhone -------------------------------
// iOS n'autorise l'autoplay que si la vidéo est `muted` + `playsinline`, et il
// consulte l'ATTRIBUT `muted` du DOM. React, lui, ne pose que la PROPRIÉTÉ pour
// la prop `muted` : l'attribut est absent du HTML et Safari refuse la lecture.
// D'où `ensureMuted()`, appelé avant chaque `play()`.
// Il reste des cas où iOS refuse quand même (mode économie d'énergie, onglet
// restauré) : on met alors la vidéo en file d'attente et on la relance au
// premier geste de l'utilisateur. Un seul listener partagé pour toutes les
// cartes, retiré dès qu'il a servi.
const pendingPlays = new Set();
let gestureListening = false;

function armGestureRetry() {
  if (gestureListening) return;
  gestureListening = true;
  const fire = () => {
    gestureListening = false;
    document.removeEventListener("touchend", fire);
    document.removeEventListener("pointerdown", fire);
    pendingPlays.forEach((v) => {
      ensureMuted(v);
      const p = v.play();
      if (p && p.catch) p.catch(() => {});
    });
    pendingPlays.clear();
  };
  document.addEventListener("touchend", fire, { passive: true });
  document.addEventListener("pointerdown", fire, { passive: true });
}

function ensureMuted(v) {
  if (v.muted && !v.hasAttribute("muted")) v.setAttribute("muted", "");
}

function safePlay(v) {
  if (!v) return;
  ensureMuted(v);
  const p = v.play();
  if (p && typeof p.catch === "function") {
    p.catch(() => {
      pendingPlays.add(v);
      armGestureRetry();
    });
  }
}

function VideosSection() {
  const ref = useReveal();
  const [activeTheme, setActiveTheme] = useState("Tous");
  // Only one card may carry sound at a time.
  const [soundId, setSoundId] = useState(null);
  const railRef = useRef(null);
  // Passe à true au premier clic sur un filtre : l'apparition en cascade ne
  // concerne que l'arrivée initiale dans le viewport, pas les changements de filtre.
  const [filtered, setFiltered] = useState(false);

  // useReveal ne monte son IntersectionObserver qu'au premier rendu : les cartes
  // créées par un changement de filtre ne seraient jamais observées et
  // resteraient invisibles. On les révèle directement — au clic, le rail est
  // forcément à l'écran.
  useEffect(() => {
    if (!filtered) return;
    const rail = railRef.current;
    if (!rail) return;
    rail.querySelectorAll(".reveal-up").forEach((n) => n.classList.add("is-in"));
  }, [filtered, activeTheme]);

  const themes = ["Tous", ...Array.from(new Set(VIDEOS.map((v) => v.theme)))];
  const shown =
    activeTheme === "Tous" ? VIDEOS : VIDEOS.filter((v) => v.theme === activeTheme);

  const scrollRail = (dir) => {
    const rail = railRef.current;
    if (!rail) return;
    const card = rail.querySelector(".video-card");
    const step = card ? card.offsetWidth + 24 : 320;
    rail.scrollBy({ left: dir * step, behavior: "smooth" });
  };

  return (
    <section ref={ref} id="conseils" className="section section-videos" data-bg-num="02">
      <div className="shell">
        <SectionHeader
          number="02"
          eyebrow="Conseils actionnables · Format court"
          title={[
            { text: "Des conseils", joinNext: true },
            { text: "applicables", blue: true },
            // `.br-mobile` est en display:none au-dessus de 640px : une seule
            // ligne en desktop, « matin. » décroché en mobile.
            {
              text: (
                <React.Fragment>
                  dès demain <br className="br-mobile" />matin.
                </React.Fragment>
              ),
            },
          ]}
        />

        <div className="reveal-up videos-intro" style={{ transitionDelay: "200ms" }}>
          <p>
            Pas de théorie. Chaque vidéo traite{" "}
            <strong style={{ fontWeight: 700, color: "var(--ink)" }}>un problème concret</strong>{" "}
            de gestion de club et donne la méthode pour le régler.
          </p>
        </div>

        <div className="videos-toolbar reveal-up" style={{ transitionDelay: "300ms" }}>
          <div className="video-filters" role="group" aria-label="Filtrer par thème">
            {themes.map((t) => (
              <button
                key={t}
                type="button"
                onClick={() => {
                  setActiveTheme(t);
                  setFiltered(true);
                }}
                className={`video-filter mono ${activeTheme === t ? "is-active" : ""}`}
                aria-pressed={activeTheme === t}
                data-hover
              >
                {t}
                {t === "Tous" && <span className="video-filter-count">{VIDEOS.length}</span>}
              </button>
            ))}
          </div>

          <RailNav onScroll={scrollRail} />
        </div>

        <div className="video-rail" ref={railRef}>
          {shown.map((v, i) => (
            <VideoCard
              key={v.id}
              video={v}
              revealDelay={filtered ? 0 : 360 + i * 80}
              soundOn={soundId === v.id}
              dimmed={soundId !== null && soundId !== v.id}
              onSound={() => setSoundId((cur) => (cur === v.id ? null : v.id))}
            />
          ))}
        </div>

        {/* En mobile la nav de la barre d'outils est masquée : on la réaffiche
            sous le rail, seul endroit où elle signale clairement le défilement
            horizontal. */}
        <RailNav className="video-rail-nav--mobile" onScroll={scrollRail} />
      </div>
    </section>
  );
}

function RailNav({ className = "", onScroll }) {
  return (
    <div className={`video-rail-nav ${className}`.trim()}>
      <button
        type="button"
        className="video-rail-btn"
        onClick={() => onScroll(-1)}
        aria-label="Vidéos précédentes"
        data-hover
      >
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <path d="M19 12H5M11 19l-7-7 7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </button>
      <button
        type="button"
        className="video-rail-btn"
        onClick={() => onScroll(1)}
        aria-label="Vidéos suivantes"
        data-hover
      >
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <path d="M5 12h14M13 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </button>
    </div>
  );
}

function VideoCard({ video, revealDelay, soundOn, dimmed, onSound }) {
  const cardRef = useRef(null);
  const videoRef = useRef(null);
  const pausedByUser = useRef(false);
  // Lues par l'IntersectionObserver, qui ne se rattache pas à chaque rendu :
  // sans refs il travaillerait sur des valeurs périmées.
  const dimmedRef = useRef(dimmed);
  const onScreen = useRef(false);
  const [playing, setPlaying] = useState(false);
  const [progress, setProgress] = useState(0);

  // The mp4 is never referenced in the markup: the src is attached only when
  // the card approaches the viewport, so the page load stays light.
  const arm = useCallback(() => {
    const v = videoRef.current;
    if (v && !v.getAttribute("src")) {
      v.setAttribute("src", video.src);
      // `preload="none"` : certaines versions d'iOS n'amorcent pas le
      // chargement sur le seul changement de src. `load()` le force.
      v.load();
    }
  }, [video.src]);

  useEffect(() => {
    const card = cardRef.current;
    const v = videoRef.current;
    if (!card || !v) return;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    // L'attribut doit être posé dès le montage : iOS lit le HTML, pas la
    // propriété React. `defaultMuted` reflète l'attribut dans le DOM.
    v.defaultMuted = true;
    v.muted = true;
    v.setAttribute("muted", "");

    // Play only what is on screen — keeps CPU and bandwidth down with 8 loops.
    const io = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          onScreen.current = true;
          if (reduce || pausedByUser.current || dimmedRef.current) return;
          arm();
          safePlay(v);
        } else {
          onScreen.current = false;
          v.pause();
        }
      },
      { rootMargin: "150px", threshold: 0.25 }
    );
    io.observe(card);

    const onPlay = () => setPlaying(true);
    const onPause = () => setPlaying(false);
    const onTime = () => {
      if (v.duration && isFinite(v.duration)) {
        setProgress((v.currentTime / v.duration) * 100);
      }
    };
    v.addEventListener("play", onPlay);
    v.addEventListener("pause", onPause);
    v.addEventListener("timeupdate", onTime);

    return () => {
      io.disconnect();
      v.removeEventListener("play", onPlay);
      v.removeEventListener("pause", onPause);
      v.removeEventListener("timeupdate", onTime);
    };
  }, [arm]);

  useEffect(() => {
    const v = videoRef.current;
    if (!v) return;
    v.muted = !soundOn;
    if (soundOn) {
      // Le son ne peut être activé que sur un geste : on retire l'attribut,
      // sinon iOS peut re-couper la vidéo au prochain `play()`.
      v.removeAttribute("muted");
      pausedByUser.current = false;
      arm();
      safePlay(v);
    }
  }, [soundOn, arm]);

  // Focus sonore : dès qu'une autre vidéo prend le son, celle-ci se met en pause.
  // Elle repart seule quand le focus est levé — sauf si l'utilisateur l'avait
  // mise en pause lui-même, ou si elle est hors écran.
  useEffect(() => {
    dimmedRef.current = dimmed;
    const v = videoRef.current;
    if (!v) return;
    if (dimmed) {
      v.pause();
      return;
    }
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce || pausedByUser.current || !onScreen.current) return;
    arm();
    safePlay(v);
  }, [dimmed, arm]);

  const togglePlay = () => {
    const v = videoRef.current;
    if (!v) return;
    if (v.paused) {
      pausedByUser.current = false;
      arm();
      safePlay(v);
    } else {
      pausedByUser.current = true;
      v.pause();
    }
  };

  const toggleSound = (e) => {
    e.preventDefault();
    e.stopPropagation();
    onSound();
  };

  const restart = (e) => {
    e.preventDefault();
    e.stopPropagation();
    const v = videoRef.current;
    if (!v) return;
    v.currentTime = 0;
    pausedByUser.current = false;
    arm();
    safePlay(v);
  };

  return (
    <article
      ref={cardRef}
      className="video-card reveal-up"
      style={{ "--reveal-delay": `${revealDelay}ms` }}
    >
      <div
        /* La classe reste portée par le média : l'<article> est en `.reveal-up`
           et son `is-in` est ajouté impérativement — un className dynamique
           dessus serait réécrit par React et ferait disparaître la carte. */
        className={`video-card-media ${dimmed ? "is-dimmed" : ""}`}
        role="button"
        tabIndex={0}
        aria-label={`${playing ? "Mettre en pause" : "Lire"} : ${video.title}`}
        onClick={togglePlay}
        onKeyDown={(e) => {
          if (e.key === "Enter" || e.key === " ") {
            e.preventDefault();
            togglePlay();
          }
        }}
        data-hover
      >
        {/* `webkit-playsinline` : anciennes versions d'iOS, sans quoi la vidéo
            passe en plein écran au lieu de jouer dans la carte — et l'autoplay
            est alors refusé. */}
        <video
          ref={videoRef}
          className="video-card-video"
          poster={video.poster}
          autoPlay
          muted
          loop
          playsInline
          webkit-playsinline="true"
          preload="none"
          aria-label={video.title}
        />

        <button
          type="button"
          className="video-card-sound"
          onClick={toggleSound}
          aria-label={soundOn ? "Couper le son" : "Activer le son"}
          aria-pressed={soundOn}
          data-hover
        >
          {soundOn ? (
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M11 5L6 9H2v6h4l5 4V5z" />
              <path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
              <path d="M19.07 4.93a10 10 0 0 1 0 14.14" />
            </svg>
          ) : (
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M11 5L6 9H2v6h4l5 4V5z" />
              <line x1="22" y1="9" x2="16" y2="15" />
              <line x1="16" y1="9" x2="22" y2="15" />
            </svg>
          )}
        </button>

        <button
          type="button"
          className="video-card-restart"
          onClick={restart}
          aria-label="Revoir la vidéo depuis le début"
          data-hover
        >
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M3 12a9 9 0 1 0 2.64-6.36L3 8" />
            <path d="M3 3v5h5" />
          </svg>
        </button>

        {!playing && (
          <span className="video-card-play" aria-hidden="true">
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
              <path d="M5.5 3.5L14 9l-8.5 5.5V3.5z" fill="currentColor" />
            </svg>
          </span>
        )}

        <span className="video-card-duration mono" aria-hidden="true">
          {video.duration}
        </span>

        <span className="video-card-progress" aria-hidden="true">
          <span className="video-card-progress-fill" style={{ width: progress + "%" }} />
        </span>
      </div>

      <span className="video-card-theme mono">{video.theme}</span>
      <h3 className="video-card-title">{video.title}</h3>
      <p className="video-card-pitch">{video.pitch}</p>
    </article>
  );
}

Object.assign(window, { VideosSection, VideoCard, VIDEOS });
