/* =====================================================================
   BOOK 3D — livre interactif (couverture + 4e de couv réelles)
   · rotation libre au doigt / souris (comme le globe)
   · auto-rotation continue (reprend après 2,5 s d'inactivité)
   · zoom molette + pinch 2 doigts (0.6× → 2.6×), sans boutons
   · épaisseur réelle (400 pages) : tranches papier + dos
   · vignettes type Amazon : couverture, 4e de couv, 5 extraits (bientôt)
   · clic → lecture plein écran (lightbox)
   Expose window.Book3D. Chargé via <script type="text/babel" src>.
   ===================================================================== */
(function(){
  const { useState, useRef, useEffect } = React;

  /* ---- Extraits PDF (rendu live via pdf.js, chargement paresseux) ---- */
  const PDF_URL = "book/apercu/extraits.pdf";
  const N_EXCERPTS = 5;
  let libPromise = null, docPromise = null;
  function loadLib(){
    if (window.pdfjsLib) return Promise.resolve(window.pdfjsLib);
    if (libPromise) return libPromise;
    libPromise = new Promise((res, rej) => {
      const s = document.createElement("script");
      s.src = "https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.min.js";
      s.onload = () => { window.pdfjsLib.GlobalWorkerOptions.workerSrc =
        "https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js"; res(window.pdfjsLib); };
      s.onerror = () => rej(new Error("pdfjs load failed"));
      document.head.appendChild(s);
    });
    return libPromise;
  }
  function getDoc(){ if (!docPromise) docPromise = loadLib().then(l => l.getDocument({ url: PDF_URL }).promise); return docPromise; }
  async function drawPdf(canvas, pageNum, cssHeight, signal){
    const doc = await getDoc();
    if (signal && signal.cancelled) return;
    const page = await doc.getPage(pageNum);
    if (signal && signal.cancelled) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2.5);
    const base = page.getViewport({ scale: 1 });
    const vp = page.getViewport({ scale: (cssHeight / base.height) * dpr });
    canvas.width = vp.width; canvas.height = vp.height;
    canvas.style.height = cssHeight + "px";
    canvas.style.width = cssHeight * (base.width / base.height) + "px";
    const ctx = canvas.getContext("2d");
    ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, vp.width, vp.height);
    const task = page.render({ canvasContext: ctx, viewport: vp });
    if (signal) signal.task = task;
    await task.promise;
  }

  // Vignette d'un extrait (page PDF), rendu paresseux a l'approche du viewport.
  function PdfThumb({ page, onClick, label }){
    const ref = useRef(null), wrapRef = useRef(null);
    const [loading, setLoading] = useState(true);
    useEffect(() => {
      let done = false; const signal = { cancelled: false };
      const render = () => { if (done || !ref.current) return; done = true;
        drawPdf(ref.current, page, 148, signal).then(() => setLoading(false)).catch(() => {}); };
      let io = null;
      if (wrapRef.current && window.IntersectionObserver){
        io = new IntersectionObserver((es) => es.forEach(e => { if (e.isIntersecting){ render(); io.disconnect(); } }), { rootMargin: "400px" });
        io.observe(wrapRef.current);
      } else render();
      return () => { signal.cancelled = true; if (io) io.disconnect(); };
    }, [page]);
    return (
      <button ref={wrapRef} className={"b3d-thumb"+(loading?" b3d-loading":"")} onClick={onClick} aria-label={label} title={label}>
        <canvas ref={ref} />
      </button>
    );
  }

  // Grand rendu d'un extrait dans la lightbox.
  function PdfCanvas({ page }){
    const ref = useRef(null);
    useEffect(() => {
      const signal = { cancelled: false, task: null };
      const draw = () => { if (ref.current) drawPdf(ref.current, page, Math.min(window.innerHeight * 0.82, 1100), signal).catch(() => {}); };
      draw();
      const onR = () => draw(); window.addEventListener("resize", onR);
      return () => { signal.cancelled = true; try { signal.task && signal.task.cancel(); } catch(e){} window.removeEventListener("resize", onR); };
    }, [page]);
    return <canvas ref={ref} />;
  }

  const CSS = `
  .b3d-stage{ touch-action:none; cursor:grab; user-select:none; -webkit-user-select:none; }
  .b3d-stage:active{ cursor:grabbing; }
  .b3d-book{ position:relative; transform-style:preserve-3d; will-change:transform; }
  .b3d-face{ position:absolute; left:50%; top:50%; backface-visibility:hidden; border-radius:2px; }
  .b3d-cover-img{ width:100%; height:100%; object-fit:cover; border-radius:2px; display:block; }
  .b3d-pages-x{ background:
      repeating-linear-gradient(to bottom, #E8E4DA 0 1.6px, #B9B4A6 1.6px 2.6px, #DDD8CC 2.6px 3.4px); 
      box-shadow: inset 0 0 14px rgba(0,0,0,.35); }
  .b3d-pages-y{ background:
      repeating-linear-gradient(to right, #E8E4DA 0 1.6px, #BDB8AA 1.6px 2.6px, #DFDACE 2.6px 3.4px);
      box-shadow: inset 0 0 14px rgba(0,0,0,.35); }
  .b3d-spine{ background: linear-gradient(to right, #05070A, #10141B 45%, #05070A);
      border:1px solid rgba(255,255,255,.08); display:flex; align-items:center; justify-content:center; overflow:hidden; }
  .b3d-spine span{ writing-mode:vertical-rl; font-size:9px; letter-spacing:.32em; text-transform:uppercase;
      color:rgba(236,238,242,.85); white-space:nowrap; }
  .b3d-thumb{ width:56px; height:74px; border-radius:6px; overflow:hidden; flex:0 0 auto;
      border:1px solid rgba(255,255,255,.14); background:#10141B; cursor:pointer;
      transition: transform .2s ease, border-color .2s ease; position:relative; }
  .b3d-thumb:hover{ transform:translateY(-3px); border-color:rgba(107,167,255,.6); }
  .b3d-thumb img{ width:100%; height:100%; object-fit:cover; display:block; }
  .b3d-thumb canvas{ width:100%; height:100%; display:block; object-fit:cover; }
  .b3d-thumb.b3d-loading::after{ content:""; position:absolute; inset:0;
      background:linear-gradient(100deg,#10141B 30%,#1a2029 50%,#10141B 70%); background-size:200% 100%;
      animation:b3dShine 1.2s linear infinite; }
  @keyframes b3dShine{ 0%{background-position:200% 0;} 100%{background-position:-200% 0;} }
  .b3d-lightbox{ position:fixed; inset:0; z-index:120; background:rgba(4,6,9,.92);
      display:flex; align-items:center; justify-content:center; padding:28px;
      backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
      animation:b3dFade .28s cubic-bezier(.2,.7,.2,1); }
  @keyframes b3dFade{ from{opacity:0;} to{opacity:1;} }
  /* conteneur = taille exacte du média : la croix et les fleches s'accrochent a ses rebords */
  .b3d-media{ position:relative; display:inline-block; line-height:0; }
  .b3d-lb-img, .b3d-media canvas{ display:block; max-width:min(92vw,760px); max-height:82vh;
      width:auto; height:auto; object-fit:contain; border-radius:3px; background:#fff;
      box-shadow:0 40px 100px -20px rgba(0,0,0,.85); }
  .b3d-lb-btn{ position:absolute; border-radius:9999px; display:flex; align-items:center; justify-content:center;
      color:#ECEEF2; background:#0B0D11; border:1px solid rgba(255,255,255,.22);
      box-shadow:0 8px 24px -8px rgba(0,0,0,.85); cursor:pointer; transition:transform .18s ease, background .18s ease; }
  .b3d-lb-btn:hover{ transform:scale(1.06); background:#151922; }
  .b3d-lb-close{ top:-16px; right:-16px; width:44px; height:44px; }
  .b3d-lb-nav{ top:50%; transform:translateY(-50%); width:46px; height:46px; }
  .b3d-lb-nav:hover{ transform:translateY(-50%) scale(1.06); }
  .b3d-lb-prev{ left:-22px; }
  .b3d-lb-next{ right:-22px; }
  .b3d-lb-count{ position:absolute; left:50%; transform:translateX(-50%); bottom:-34px;
      font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:.16em; color:rgba(236,238,242,.6); white-space:nowrap; }
  `;

  const L10N = {
    fr: { cover:"Couverture", back:"4e de couverture", excerpt:"Extrait", soon:"Bientôt", hint:"Fais tourner le livre · pince ou molette pour zoomer · clique pour lire", close:"Fermer", prevA:"Précédent", nextA:"Suivant" },
    en: { cover:"Front cover", back:"Back cover", excerpt:"Excerpt", soon:"Soon", hint:"Spin the book · pinch or scroll to zoom · click to read", close:"Close", prevA:"Previous", nextA:"Next" },
  };

  function Book3D({ lang }){
    const t = L10N[lang] || L10N.en;
    // Faces rendues en HAUTE résolution intrinsèque (×3) puis réduites via scale de base,
    // pour rester nettes (qualité PDF) jusqu'au zoom max. Géométrie visuelle inchangée.
    const W = 696, H = 900, T = 74; // épaisseur ~10% de la largeur (400 pages, proportion réaliste)
    const BASE = 1/3;                // ramène à ~232×300 à l'écran
    const MINS = 0.20, MAXS = 1.15;  // zoom : 0.6× → 3.45× de la taille de base
    const stageRef = useRef(null);
    const bookRef = useRef(null);
    const st = useRef({ rx:-6, ry:-28, scale:BASE, spin:true, down:false, moved:false,
      px:0, py:0, pinch:0, idleT:null, raf:0 });
    const [lb, setLb] = useState(-1); // index lightbox, -1 fermé

    // Vignette = version web légère ; plein écran (lightbox) = version haute résolution (qualité PDF).
    const IMGS = [
      { type:"img", src:"book/front-web.png", hi:"book/front-hi.png", label:t.cover },
      { type:"img", src:"book/back-web.png",  hi:"book/back-hi.png",  label:t.back },
    ];
    // Galerie complete : couverture, 4e de couv, puis les 5 extraits (pages PDF).
    const MEDIA = IMGS.concat(
      Array.from({ length: N_EXCERPTS }, (_, i) => ({ type:"pdf", page:i+1, label:t.excerpt+" "+(i+1) }))
    );

    // Clavier + verrou de scroll quand la lightbox est ouverte.
    useEffect(() => {
      if (lb < 0) return;
      const onKey = (e) => {
        if (e.key === "Escape") setLb(-1);
        else if (e.key === "ArrowLeft") setLb(v => Math.max(0, v - 1));
        else if (e.key === "ArrowRight") setLb(v => Math.min(MEDIA.length - 1, v + 1));
      };
      window.addEventListener("keydown", onKey);
      const ov = document.body.style.overflow; document.body.style.overflow = "hidden";
      return () => { window.removeEventListener("keydown", onKey); document.body.style.overflow = ov; };
    }, [lb]);

    useEffect(() => {
      const s = st.current, book = bookRef.current, stage = stageRef.current;
      if (!book || !stage) return;
      const apply = () => { book.style.transform =
        `rotateX(${s.rx}deg) rotateY(${s.ry}deg) scale3d(${s.scale},${s.scale},${s.scale})`; };
      // Projection orthographique (pas de perspective) : l'épaisseur du livre reste
      // parfaitement proportionnelle à la couverture à TOUS les niveaux de zoom.
      const loop = () => { if (s.spin && !s.down){ s.ry += 0.22; apply(); } s.raf = requestAnimationFrame(loop); };
      s.raf = requestAnimationFrame(loop);
      const wake = () => { clearTimeout(s.idleT); s.spin = false;
        s.idleT = setTimeout(()=>{ s.spin = true; }, 2500); };

      const down = (x,y) => { s.down=true; s.moved=false; s.px=x; s.py=y; wake(); };
      const move = (x,y) => { if(!s.down) return;
        const dx=x-s.px, dy=y-s.py;
        if (Math.abs(dx)+Math.abs(dy) > 3) s.moved = true;
        s.ry += dx*0.45; s.rx = Math.max(-80, Math.min(80, s.rx - dy*0.35));
        s.px=x; s.py=y; wake(); apply(); };
      const up = () => { s.down=false; wake(); };

      const onMD = e => down(e.clientX, e.clientY);
      const onMM = e => move(e.clientX, e.clientY);
      const onMU = () => up();
      const onWheel = e => { e.preventDefault(); wake();
        s.scale = Math.max(MINS, Math.min(MAXS, s.scale * (e.deltaY < 0 ? 1.07 : 0.93)));
        apply(); };
      const dist = tt => Math.hypot(tt[0].clientX-tt[1].clientX, tt[0].clientY-tt[1].clientY);
      const onTS = e => { if (e.touches.length===2){ s.pinch = dist(e.touches); s.down=false; }
        else if (e.touches.length===1) down(e.touches[0].clientX, e.touches[0].clientY); };
      const onTM = e => { e.preventDefault();
        if (e.touches.length===2){ const d = dist(e.touches);
          if (s.pinch>0){ s.scale = Math.max(MINS, Math.min(MAXS, s.scale * (d/s.pinch))); s.pinch=d; wake(); apply(); }
        } else if (e.touches.length===1 && s.pinch===0) move(e.touches[0].clientX, e.touches[0].clientY); };
      const onTE = e => { if (e.touches.length===0){ s.pinch=0; up(); } };

      stage.addEventListener("mousedown", onMD);
      window.addEventListener("mousemove", onMM);
      window.addEventListener("mouseup", onMU);
      stage.addEventListener("wheel", onWheel, { passive:false });
      stage.addEventListener("touchstart", onTS, { passive:true });
      stage.addEventListener("touchmove", onTM, { passive:false });
      stage.addEventListener("touchend", onTE);
      apply();
      return () => { cancelAnimationFrame(s.raf); clearTimeout(s.idleT);
        stage.removeEventListener("mousedown", onMD);
        window.removeEventListener("mousemove", onMM);
        window.removeEventListener("mouseup", onMU);
        stage.removeEventListener("wheel", onWheel);
        stage.removeEventListener("touchstart", onTS);
        stage.removeEventListener("touchmove", onTM);
        stage.removeEventListener("touchend", onTE); };
    }, []);

    const openIfClick = () => { if (!st.current.moved) setLb(0); };

    const face = (w,h,tf,extra,children) => (
      <div className={"b3d-face "+(extra||"")}
        style={{ width:w, height:h, marginLeft:-w/2, marginTop:-h/2, transform:tf }}>{children}</div>
    );

    return (
      <div className="flex flex-col items-center gap-5">
        <div ref={stageRef} className="b3d-stage" style={{ width:W*BASE+120, height:H*BASE+90 }} onClick={openIfClick}>
          <div ref={bookRef} className="b3d-book" style={{ width:"100%", height:"100%" }}>
            <div style={{ position:"absolute", left:"50%", top:"50%", transformStyle:"preserve-3d" }}>
              {face(W,H,`translateZ(${T/2}px)`,"", <img className="b3d-cover-img" src="book/front-hi.png" alt="Couverture — Unstoppable Revolution" draggable="false"/>)}
              {face(W,H,`rotateY(180deg) translateZ(${T/2}px)`,"", <img className="b3d-cover-img" src="book/back-hi.png" alt="4e de couverture" draggable="false"/>)}
              {face(T,H,`rotateY(-90deg) translateZ(${W/2}px)`,"b3d-spine", <span>Unstoppable Revolution · R. Leblanc</span>)}
              {face(T,H,`rotateY(90deg) translateZ(${W/2}px)`,"b3d-pages-y", null)}
              {face(W,T,`rotateX(90deg) translateZ(${H/2}px)`,"b3d-pages-x", null)}
              {face(W,T,`rotateX(-90deg) translateZ(${H/2}px)`,"b3d-pages-x", null)}
            </div>
          </div>
        </div>

        <div className="text-[10.5px] tracking-wide text-white/40 text-center px-4">{t.hint}</div>

        <div className="flex gap-2.5 flex-wrap justify-center max-w-[380px]">
          {IMGS.map((im,i)=>(
            <button key={i} className="b3d-thumb" onClick={()=>setLb(i)} aria-label={im.label} title={im.label}>
              <img src={im.src} alt={im.label} draggable="false"/>
            </button>
          ))}
          {Array.from({length:N_EXCERPTS},(_,i)=>(
            <PdfThumb key={"x"+i} page={i+1} label={t.excerpt+" "+(i+1)} onClick={()=>setLb(IMGS.length+i)} />
          ))}
        </div>

        {lb >= 0 && (() => {
          const m = MEDIA[lb];
          return ReactDOM.createPortal(
            <div className="b3d-lightbox" onClick={()=>setLb(-1)}>
              <div className="b3d-media" onClick={e=>e.stopPropagation()}>
                {m.type === "pdf"
                  ? <PdfCanvas page={m.page} />
                  : <img className="b3d-lb-img" src={m.hi || m.src} alt={m.label} draggable="false"/>}

                {/* Croix — angle superieur droit de l'image */}
                <button className="b3d-lb-btn b3d-lb-close" onClick={()=>setLb(-1)} aria-label={t.close}>
                  <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>
                </button>

                {/* Fleches — sur les rebords de l'image */}
                {lb > 0 &&
                  <button className="b3d-lb-btn b3d-lb-nav b3d-lb-prev" onClick={()=>setLb(lb-1)} aria-label={t.prevA}>
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M15 6l-6 6 6 6"/></svg>
                  </button>}
                {lb < MEDIA.length-1 &&
                  <button className="b3d-lb-btn b3d-lb-nav b3d-lb-next" onClick={()=>setLb(lb+1)} aria-label={t.nextA}>
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M9 6l6 6-6 6"/></svg>
                  </button>}

                <div className="b3d-lb-count">{m.label} · {lb+1}/{MEDIA.length}</div>
              </div>
            </div>,
            document.body
          );
        })()}
      </div>
    );
  }

  // injecte le CSS une fois
  if (!document.getElementById("b3d-css")){
    const el = document.createElement("style"); el.id="b3d-css"; el.textContent = CSS;
    document.head.appendChild(el);
  }
  window.Book3D = Book3D;
})();
