import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { QueryClientProvider } from "@tanstack/react-query";
import { SpeedInsights } from "@vercel/speed-insights/react";
import App from "./app/App";
import { queryClient } from "./lib/queryClient";
import "./styles/index.css";

// Polyfill Promise.withResolvers (ES2024) for iPadOS 16 + iPadOS 17.0–17.3.
// pdfjs-dist v5 calls this at top level of its module, so OFP upload throws
// TypeError on every iPad before iOS 17.4 if the polyfill isn't installed
// before App / any pdfjs import resolves.
if (typeof (Promise as any).withResolvers !== 'function') {
  (Promise as any).withResolvers = function <T>() {
    let resolve!: (value: T | PromiseLike<T>) => void;
    let reject!: (reason?: any) => void;
    const promise = new Promise<T>((res, rej) => { resolve = res; reject = rej; });
    return { promise, resolve, reject };
  };
}

// Register service worker for PWA + push notifications
// Handles automatic updates: detects new SW → skipWaiting → smooth reload.
// Also checks for updates on foreground resume (critical for iOS).
if ('serviceWorker' in navigator) {
  let reloading = false;

  // When the new SW takes control, stamp localStorage so the version
  // check hook knows we just updated and can skip the first check.
  navigator.serviceWorker.addEventListener('controllerchange', () => {
    if (reloading) return;
    reloading = true;
    try { localStorage.setItem('sw_updated_at', Date.now().toString()); } catch {}
    const overlay = document.createElement('div');
    overlay.setAttribute('style',
      'position:fixed;inset:0;z-index:99999;background:#020617;display:flex;' +
      'align-items:center;justify-content:center;color:white;font-family:system-ui;' +
      'font-size:1.1rem;letter-spacing:0.02em'
    );
    overlay.textContent = 'Updating\u2026';
    document.body.appendChild(overlay);
    setTimeout(() => window.location.reload(), 300);
  });

  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js', { updateViaCache: 'none' })
      .then((registration) => {
        const applyUpdate = (sw: ServiceWorker) => {
          sw.postMessage({ type: 'SKIP_WAITING' });
        };

        // SW already waiting on page load
        if (registration.waiting) {
          applyUpdate(registration.waiting);
          return;
        }

        // New SW installing
        registration.addEventListener('updatefound', () => {
          const newSW = registration.installing;
          if (!newSW) return;
          newSW.addEventListener('statechange', () => {
            if (newSW.state === 'installed') {
              applyUpdate(newSW);
            }
          });
        });

        // Poll for updates every 5 minutes while tab is active.
        // Matches the version check interval — no point in SW polling faster
        // than the banner can react, and 60s was wasting bandwidth + CPU.
        setInterval(() => registration.update(), 5 * 60_000);

        // Check for updates when app returns to foreground (critical for iOS PWA)
        document.addEventListener('visibilitychange', () => {
          if (document.visibilityState === 'visible') {
            registration.update();
          }
        });
      })
      .catch(() => {
        // SW registration failed — not critical
      });
  });
}

createRoot(document.getElementById("root")!).render(
  <QueryClientProvider client={queryClient}>
    <BrowserRouter>
      <App />
      <SpeedInsights />
    </BrowserRouter>
  </QueryClientProvider>
);

// Fade out the boot splash once React has painted. Keep it up for a brief
// minimum so the logo animation reads as intentional rather than a flash.
(() => {
  const splash = document.getElementById('splash');
  if (!splash) return;
  const SHOWN_AT = performance.now();
  const MIN_VISIBLE = 700;
  const dismiss = () => {
    const wait = Math.max(0, MIN_VISIBLE - (performance.now() - SHOWN_AT));
    setTimeout(() => {
      splash.classList.add('is-hiding');
      setTimeout(() => splash.remove(), 500);
    }, wait);
  };
  requestAnimationFrame(() => requestAnimationFrame(dismiss));
})();
  