import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { HelmetProvider } from 'react-helmet-async'
import App from './App.tsx'
import './index.css'

import { initAttributionTracking } from "@/hooks/useAttribution";
import { WORDMARK_PRELOAD } from "@/components/ResponsiveImage";

// Preload the LCP wordmark image (AVIF) so the browser fetches it in parallel
// with the JS bundle. Injected from JS because Vite fingerprints the asset URL.
if (typeof document !== "undefined") {
  const preload = document.createElement("link");
  preload.rel = "preload";
  preload.as = "image";
  preload.type = "image/avif";
  preload.href = WORDMARK_PRELOAD.avif;
  (preload as HTMLLinkElement & { fetchPriority?: string }).fetchPriority = "high";
  document.head.appendChild(preload);
}

// Service worker: single guarded registrar (handles preview/iframe/dev
// unregistration and swallows environmental registration failures).
import { registerServiceWorker } from "@/lib/pwa-register";
registerServiceWorker();


// Global safety net: catch stale dynamic-import chunk errors after a redeploy.
// Strict match — generic "Failed to fetch" (Supabase/edge calls/etc.) MUST NOT
// trigger a reload. The route-level lazyWithRetry retries first; this guard is
// the last resort and reloads at most once per session.
if (typeof window !== 'undefined') {
  const SESSION_FLAG = 'lovable:chunk-reloaded-this-session';

  const isChunkErrorMsg = (msg: string) =>
    msg.includes('Failed to fetch dynamically imported module') ||
    msg.includes('dynamically imported module') ||
    msg.includes('Loading chunk');

  const maybeReload = (msg: string, source: string) => {
    if (!isChunkErrorMsg(msg)) return;
    if (sessionStorage.getItem(SESSION_FLAG)) {
      console.warn(`[chunk-guard] already reloaded once this session (${source}):`, msg);
      return;
    }
    sessionStorage.setItem(SESSION_FLAG, String(Date.now()));
    window.location.reload();
  };

  window.addEventListener('vite:preloadError', (e) => {
    // Let lazyWithRetry handle its own retries; only act if we haven't
    // reloaded yet this session.
    if (!sessionStorage.getItem(SESSION_FLAG)) {
      e.preventDefault();
    }
    maybeReload('Failed to fetch dynamically imported module', 'vite:preloadError');
  });
  window.addEventListener('error', (e) => maybeReload(String(e?.message ?? ''), 'window.error'));
  window.addEventListener('unhandledrejection', (e: PromiseRejectionEvent) => {
    const reason = e.reason;
    maybeReload(reason instanceof Error ? reason.message : String(reason ?? ''), 'unhandledrejection');
  });
}




// Initialize attribution tracking on app load
initAttributionTracking();

// Lazy-init marketing pixels (Google Ads, Meta, TikTok, GA4) only once the
// page has loaded AND the main thread is idle, so their script evaluation
// never lands inside the first-paint window (Sentry "Blocking Operation").
if (typeof window !== "undefined") {
  const startPixels = () => {
    const run = () => import("./lib/pixels").then(m => m.initPixels()).catch(() => {});
    const ric = (window as any).requestIdleCallback as
      | ((cb: () => void, o?: { timeout?: number }) => number)
      | undefined;
    if (ric) ric(run, { timeout: 5000 });
    else setTimeout(run, 1500);
  };
  if (document.readyState === "complete") startPixels();
  else window.addEventListener("load", startPixels, { once: true });
}

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <HelmetProvider>
      <App />
    </HelmetProvider>
  </StrictMode>
);

// Lazy-init Sentry + Web Vitals reporter after first paint so they stay off the critical path.
if (typeof window !== "undefined") {
  const bootDeferred = () => {
    import("./lib/sentry-init").then((m) => m.initSentry()).catch(() => {});
    import("./lib/web-vitals-reporter").then((m) => m.startWebVitalsReporter()).catch(() => {});
  };
  const ric = (window as Window & {
    requestIdleCallback?: (cb: () => void, opts?: { timeout?: number }) => number;
  }).requestIdleCallback as
    | ((cb: () => void, opts?: { timeout?: number }) => number)
    | undefined;
  if (ric) ric(bootDeferred, { timeout: 4000 });
  else setTimeout(bootDeferred, 2000);
}

