/* ============================================================
   beedle dataroom — full-screen page viewer ("present" mode)
   ------------------------------------------------------------
   One page at a time, fit-to-CONTAIN on a dark full-bleed stage.
   Tap the right ~65% to advance, the left ~35% to go back (faint
   chevrons hint both); a subtle ✕ / Esc / browser-Back / rotating
   to portrait exits. Built for phones in landscape but available on
   any device via the viewer's "Present" button (see viewer.jsx) and
   wired as its own back-stack layer in app.jsx.

   Generic over page count:
     • uploaded PDF → pdf.js, one page rendered to a contained canvas
       (PdfContainPage); total = pdf.numPages (doc.pages is unreliable
       for uploads, always 1).
     • mock investor deck (type "ppt", no file) → PPT_SLIDES one at a time.
     • mock multi-page pdf/image docs → <DocContent page={n}/> letterboxed.
   Real uploaded .pptx stays download-only until the deferred server-side
   PPT→PDF conversion lands (then it flows through the PDF branch).
   Sizing/clamp helpers (fitContainScale, clampPage) live in pdf-view.js
   so they're unit-tested without a browser.
   ============================================================ */

// One PDF page rendered to a canvas sized to fit WHOLE inside its host box
// (contain), re-fitting on rotation/resize. Eager + single-page — the sibling
// PdfFilePage is fit-to-width + lazy + stacked for the continuous reader.
function PdfContainPage({ pdf, num }) {
  const hostRef = React.useRef(null);
  const canvasRef = React.useRef(null);
  const [box, setBox] = React.useState({ w: 0, h: 0 });
  const [fit, setFit] = React.useState(null); // {scale,w,h} of the on-screen page — sizes the wrap + the link overlay

  React.useEffect(() => {
    const el = hostRef.current;
    if (!el) return;
    const measure = () => setBox({ w: el.clientWidth, h: el.clientHeight });
    measure();
    if (typeof ResizeObserver === "undefined") return;
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  React.useEffect(() => {
    if (!pdf || !(box.w > 0) || !(box.h > 0)) return;
    let dead = false, task = null;
    (async () => {
      try {
        const page = await pdf.getPage(num);
        if (dead) return;
        const base = page.getViewport({ scale: 1 });
        const css = fitContainScale(box.w, box.h, base.width, base.height, { gutter: 8 });
        const vp = page.getViewport({ scale: renderScale(css, window.devicePixelRatio) });
        const canvas = canvasRef.current;
        if (!canvas) return;
        canvas.width = vp.width; canvas.height = vp.height;
        // backing store may be 2× (retina); pin the on-screen size to the css fit.
        canvas.style.width = (base.width * css) + "px";
        canvas.style.height = (base.height * css) + "px";
        if (!dead) setFit({ scale: css, w: base.width * css, h: base.height * css });
        task = page.render({ canvasContext: canvas.getContext("2d"), viewport: vp });
        await task.promise;
      } catch (e) { /* cancelled render / destroyed doc — keep the frame */ }
    })();
    return () => { dead = true; if (task) task.cancel(); };
  }, [pdf, num, box.w, box.h]);

  return (
    <div ref={hostRef} className="pv-pdf">
      {/* canvas + link overlay share one box so the <a>s land exactly over the page */}
      <div className="pv-page" style={fit ? { width: fit.w + "px", height: fit.h + "px" } : undefined}>
        <canvas ref={canvasRef} className="pv-canvas" />
        {fit && <PdfLinkLayer pdf={pdf} num={num} scale={fit.scale} />}
      </div>
    </div>
  );
}

// Fullscreen API shim, resolved once. Lets the page viewer surrender the
// browser chrome (the heavy Safari/Chrome toolbar) on platforms that support
// it — Android Chrome, desktop, iPad. iPhone Safari exposes NO element
// Fullscreen API, so this resolves to null there and the button is hidden
// rather than dead (iPhone can only drop the toolbar via Add-to-Home-Screen).
const FS = (() => {
  if (typeof document === "undefined") return null;
  const el = document.documentElement;
  const request = el.requestFullscreen || el.webkitRequestFullscreen;
  const exit = document.exitFullscreen || document.webkitExitFullscreen;
  const enabled = document.fullscreenEnabled != null ? document.fullscreenEnabled : document.webkitFullscreenEnabled;
  if (!request || !exit || enabled === false) return null;
  return {
    request: (node) => (node.requestFullscreen || node.webkitRequestFullscreen).call(node),
    exit: () => (document.exitFullscreen || document.webkitExitFullscreen).call(document),
    current: () => document.fullscreenElement || document.webkitFullscreenElement || null,
    event: ("onwebkitfullscreenchange" in document) ? "webkitfullscreenchange" : "fullscreenchange",
  };
})();

// iPhone Safari has no element Fullscreen API, but it collapses its toolbar when
// the DOCUMENT scrolls. So where real fullscreen is unavailable on a touch device
// we lay the viewer out as a tall page that scrolls the document, with a visible
// scroll pad below the slide to swipe down on. A no-op everywhere the Fullscreen
// button works (Android/desktop/iPad).
const wantsScrollToFill = !FS && typeof window !== "undefined" && !!window.matchMedia && window.matchMedia("(pointer: coarse)").matches;

function PageViewer({ doc, onClose }) {
  const rootRef = React.useRef(null);
  const isPdf = !!doc.hasFile && doc.type === "pdf";
  const isMockPpt = doc.type === "ppt" && !doc.hasFile;
  const [pdf, setPdf] = React.useState(null);
  const [pdfErr, setPdfErr] = React.useState(null);
  const [cur, setCur] = React.useState(1);

  // total slides: pdf → numPages once loaded; mock deck → its length; else doc.pages.
  const total = isPdf ? (pdf ? pdf.numPages : 1)
    : isMockPpt ? PPT_SLIDES.length
    : Math.max(1, doc.pages || 1);

  React.useEffect(() => { setCur(1); }, [doc.id]);

  // A second getDocument for present mode (single-page lifecycle, distinct from
  // the continuous PdfFileDoc). Bytes are HTTP-cached, so the reload is cheap.
  React.useEffect(() => {
    if (!isPdf || !window.pdfjsLib) return;
    let dead = false, loaded = null;
    (async () => {
      try {
        await ensurePdfWorker(window.pdfjsLib, PDFJS_WORKER_URL);
        loaded = await window.pdfjsLib.getDocument({ url: api.rawUrl(doc.id), withCredentials: true }).promise;
        if (dead) { loaded.destroy(); return; }
        setPdf(loaded);
      } catch (e) { if (!dead) setPdfErr(e && e.message ? e.message : String(e)); }
    })();
    return () => { dead = true; if (loaded) loaded.destroy(); };
  }, [doc.id, isPdf]);

  const go = React.useCallback((dir) => setCur((c) => clampPage(c + dir, total)), [total]);

  React.useEffect(() => {
    const h = (e) => {
      if (e.key === "Escape") onClose();
      else if (e.key === "ArrowRight" || e.key === " " || e.key === "Spacebar") { e.preventDefault(); go(1); }
      else if (e.key === "ArrowLeft") go(-1);
    };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [go, onClose]);

  // swipe: horizontal drag past a threshold pages prev/next (touch is primary here)
  const touch = React.useRef(null);
  const onTouchStart = (e) => { const t = e.touches[0]; touch.current = { x: t.clientX, y: t.clientY }; };
  const onTouchEnd = (e) => {
    const s = touch.current; if (!s) return; touch.current = null;
    const t = e.changedTouches[0];
    const dx = t.clientX - s.x, dy = t.clientY - s.y;
    if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) go(dx < 0 ? 1 : -1);
  };

  // Real fullscreen (drops the browser toolbar) where the platform allows it.
  // requestFullscreen needs a user gesture, so it's wired to an explicit button
  // tap — not the auto-rotate open. Exits automatically when the viewer closes.
  const [fs, setFs] = React.useState(false);
  React.useEffect(() => {
    if (!FS) return;
    const onChange = () => setFs(FS.current() === rootRef.current);
    document.addEventListener(FS.event, onChange);
    return () => {
      document.removeEventListener(FS.event, onChange);
      if (FS.current()) { try { FS.exit(); } catch (_) {} }
    };
  }, []);
  const toggleFs = () => {
    if (!FS) return;
    try { FS.current() === rootRef.current ? FS.exit() : FS.request(rootRef.current); } catch (_) {}
  };

  // Scroll-to-fill (iPhone): while open, darken html/body and stop the rubber-band
  // (so a swipe at the top doesn't bounce the app behind into view), and start at
  // the top so the toolbar is up with room to scroll it away.
  React.useEffect(() => {
    if (!wantsScrollToFill) return;
    const de = document.documentElement, b = document.body;
    de.classList.add("pv-scroll-lock"); b.classList.add("pv-scroll-lock");
    try { window.scrollTo(0, 0); } catch (_) {}
    return () => { de.classList.remove("pv-scroll-lock"); b.classList.remove("pv-scroll-lock"); };
  }, []);

  let slide;
  if (isPdf) {
    slide = pdfErr ? <div className="pv-msg">{T("Couldn’t load this document.")}</div>
      : pdf ? <PdfContainPage pdf={pdf} num={cur} />
      : <div className="pv-msg">{T("Loading document…")}</div>;
  } else if (isMockPpt) {
    slide = <div className="pv-ppt"><PptSlide slide={PPT_SLIDES[cur - 1]} idx={cur - 1} total={total} /></div>;
  } else {
    slide = <div className="pv-doc scroll"><DocContent doc={doc} page={cur} /></div>;
  }

  return (
    <div className={"page-viewer" + (wantsScrollToFill ? " pv-scroll" : "")} ref={rootRef} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
      <div className="pv-stage">
        {slide}
        {/* invisible tap zones with a faint directional hint; disabled at the ends (no wrap) */}
        <button className="pv-tap pv-tap-left" onClick={() => go(-1)} disabled={cur <= 1} tabIndex={-1} aria-label={T("Previous")}>
          <Icon name="chevL" size={28} />
        </button>
        <button className="pv-tap pv-tap-right" onClick={() => go(1)} disabled={cur >= total} tabIndex={-1} aria-label={T("Next")}>
          <Icon name="chevR" size={28} />
        </button>
      </div>
      {/* a real, scrollable element BELOW the slide — swipe up onto it to scroll the
          document so Safari collapses its toolbar (only in the iPhone scroll path) */}
      {total > 1 && <div className="pv-pageno tnum">{cur} / {total}</div>}
      {wantsScrollToFill && (
        <div className="pv-scroll-pad">
          <Icon name="chevD" size={16} style={{ transform: "rotate(180deg)" }} />
          {T("(C) 2026 beedle solutions Inc.)")}
        </div>
      )}
      {FS && (
        <button className="pv-fs" onClick={toggleFs} title={fs ? T("Exit full screen") : T("Full screen")} aria-label={fs ? T("Exit full screen") : T("Full screen")}>
          <Icon name="expand" size={18} />
        </button>
      )}
      <button className="pv-close" onClick={onClose} title={T("Close (Esc)")} aria-label={T("Close")}><Icon name="x" size={20} /></button>
    </div>
  );
}
Object.assign(window, { PageViewer, PdfContainPage });
