import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { supabase } from "@/integrations/supabase/client";
import { initWebVitals } from "./hooks/useWebVitals";

// Expose supabase on window so React Native WebView injected scripts can call setSession()
(window as any).supabase = supabase;

// Initialize Web Vitals monitoring - skip in native apps (wastes memory in WebView)
if (!(window as any).Capacitor && !(window as any).ReactNativeWebView) {
  initWebVitals();
}

// Early web detection - no Capacitor import needed
const isDefinitelyWeb = (): boolean => {
  if (typeof window === 'undefined') return true;
  const win = window as any;
  return !win.Capacitor;
};

// Lazy check for native platform
const checkIsNativePlatform = async (): Promise<boolean> => {
  // Fast path: if definitely web, don't try to import Capacitor
  if (isDefinitelyWeb()) return false;
  
  try {
    // @vite-ignore - prevent bundling, load only on native
    const { Capacitor } = await import(/* @vite-ignore */ '@capacitor/core');
    return Capacitor.isNativePlatform();
  } catch {
    return false;
  }
};

// Hide instant shell after React hydrates - no animation, just remove
const hideInstantShell = () => {
  const shell = document.getElementById('instant-shell');
  if (shell) shell.remove();
};

// Hide legacy HTML splash screen
const hideHtmlSplash = () => {
  const splash = document.getElementById('splash-screen');
  if (splash) splash.remove();
};

// Hide native Capacitor splash screen
const hideNativeSplash = async () => {
  const isNative = await checkIsNativePlatform();
  if (isNative) {
    try {
      // @vite-ignore - prevent bundling, load only on native
      const { SplashScreen } = await import(/* @vite-ignore */ '@capacitor/splash-screen');
      await SplashScreen.hide({ fadeOutDuration: 300 });
    } catch (e) {
      console.log('[Splash] Native splash screen not available');
    }
  }
};

// Register Service Worker AFTER React mounts (non-blocking)
const registerServiceWorker = async () => {
  // Skip SW registration in native apps - they use native caching
  const isNative = await checkIsNativePlatform();
  if (isNative) return;
  // Skip SW in React Native WebView - SW intercepts cause net::ERR_FAILED on Android
  const { isReactNativeWebView: isRNWebView } = await import("@/lib/webviewDetect");
  if (isRNWebView()) return;
  if (!("serviceWorker" in navigator)) return;
  
  // Use requestIdleCallback for non-critical work, fallback to setTimeout
  const scheduleTask = window.requestIdleCallback || ((cb: () => void) => setTimeout(cb, 1000));
  
  scheduleTask(() => {
    navigator.serviceWorker
      .register("/sw.js")
      .then((registration) => {
        console.log("[SW] Registered with scope:", registration.scope);
        
        // Check for updates periodically
        setInterval(() => {
          registration.update();
        }, 1000 * 60 * 60); // Every hour

        // Handle updates - activate new SW immediately
        registration.addEventListener("updatefound", () => {
          const newWorker = registration.installing;
          if (newWorker) {
            newWorker.addEventListener("statechange", () => {
              if (newWorker.state === "installed" && navigator.serviceWorker.controller) {
                console.log("[SW] New version available, will activate on next reload");
              }
            });
          }
        });

        // Detect if running in PWA mode
        const isPWA = window.matchMedia("(display-mode: standalone)").matches ||
                      (window.navigator as any).standalone ||
                      document.referrer.includes("android-app://");
        
        if (isPWA && registration.active) {
          registration.active.postMessage({ type: "SET_NATIVE_MODE", isNative: true });
        }
      })
      .catch((error) => {
        console.error("[SW] Registration failed:", error);
      });

    // Listen for SW update messages — force a one-time reload when CACHE_VERSION bumps
    // so iOS WKWebView picks up the latest frontend on the FIRST launch (not the second).
    navigator.serviceWorker.addEventListener("message", (event) => {
      if (event.data?.type !== "SW_UPDATED") return;
      const newVersion = String(event.data.version ?? "");
      console.log("[SW] Updated to version:", newVersion);
      if (!newVersion) return;
      try {
        const lastSeen = localStorage.getItem("vw_sw_version");
        if (lastSeen === newVersion) return;
        localStorage.setItem("vw_sw_version", newVersion);
        // Skip reload on the very first install (no previous version recorded)
        if (lastSeen === null) return;
        // Guard against reload loops within a single session
        const reloadKey = "vw_sw_reload_" + newVersion;
        if (sessionStorage.getItem(reloadKey)) return;
        sessionStorage.setItem(reloadKey, "1");
        // Defer reload until the page is fully loaded + idle so we don't tear
        // down React mid-hydration (which causes "dispatcher.useEffect null").
        const doReload = () => {
          console.log("[SW] New CACHE_VERSION detected, reloading once for fresh assets...");
          window.location.reload();
        };
        const schedule = () => setTimeout(doReload, 1500);
        if (document.readyState === "complete") {
          schedule();
        } else {
          window.addEventListener("load", schedule, { once: true });
        }
      } catch {}
    });
  });
};

// Crash recovery: detect if previous session crashed and clear corrupted state
// iOS WKWebView can leave stale caches/state after an OOM kill that prevents relaunch
const recoverFromCrash = async () => {
  const LAUNCH_KEY = 'vw_clean_launch';
  const CRASH_KEY = 'vw_crash_count';
  try {
    const didLaunchClean = sessionStorage.getItem(LAUNCH_KEY);
    if (!didLaunchClean) {
      // First load of this session — check if previous session crashed
      const crashCount = parseInt(localStorage.getItem(CRASH_KEY) || '0', 10);
      // Mark this session as "in progress" — if we crash, this won't persist in sessionStorage
      sessionStorage.setItem(LAUNCH_KEY, '1');
      
      if (crashCount >= 1) {
        // Crash detected — clear everything to recover (trigger after just 1 unclean shutdown)
        console.warn('[CrashRecovery] Crash detected (count:', crashCount, '), clearing all caches...');
        
        // Unregister ALL service workers — they may be serving corrupted cached responses
        if ('serviceWorker' in navigator) {
          try {
            const registrations = await navigator.serviceWorker.getRegistrations();
            await Promise.all(registrations.map(r => r.unregister()));
            console.log('[CrashRecovery] Unregistered', registrations.length, 'service workers');
          } catch {}
        }
        
        // Clear all Cache API storage
        if ('caches' in window) {
          const names = await caches.keys();
          await Promise.all(names.map(n => caches.delete(n)));
        }
        
        // Clear localStorage but preserve auth session
        const authKeys: [string, string | null][] = [];
        for (let i = 0; i < localStorage.length; i++) {
          const key = localStorage.key(i);
          if (key && (key.includes('supabase') || key.includes('auth'))) {
            authKeys.push([key, localStorage.getItem(key)]);
          }
        }
        localStorage.clear();
        authKeys.forEach(([k, v]) => { if (v) localStorage.setItem(k, v); });
        localStorage.setItem(CRASH_KEY, '0');
        console.log('[CrashRecovery] State cleared, app should recover.');
      } else {
        // Increment crash counter — if session ends cleanly, we reset it below
        localStorage.setItem(CRASH_KEY, String(crashCount + 1));
      }
    }
  } catch {
    // Storage access failed — ignore
  }
};

// Mark clean shutdown by resetting crash counter
const markCleanShutdown = () => {
  try {
    localStorage.setItem('vw_crash_count', '0');
  } catch {}
};

const isNativeRuntime = (): boolean => {
  const win = window as any;
  return !!win.Capacitor || !!win.ReactNativeWebView;
};

// In native/WebView, pagehide fires on background (not true app exit), so don't treat it as clean.
// Instead, only reset crash counter after the app has stayed alive for a while.
const armCleanLaunchReset = () => {
  if (isNativeRuntime()) {
    setTimeout(() => {
      markCleanShutdown();
      console.log('[CrashRecovery] Launch remained stable; crash counter reset');
    }, 20000);
    return;
  }

  // Web browser behavior: unload generally means intentional navigation/close
  window.addEventListener('beforeunload', markCleanShutdown);
};

// Run crash recovery before mounting
recoverFromCrash();
armCleanLaunchReset();

// Mount React app
const root = createRoot(document.getElementById("root")!);
root.render(<App />);

// Hide shells and register SW after React is ready
// Single rAF is enough - React has already painted
requestAnimationFrame(() => {
  hideInstantShell();
  hideHtmlSplash();
  hideNativeSplash();
  registerServiceWorker();
});
