import { createRoot } from "react-dom/client";
import { Capacitor } from "@capacitor/core";
import { MotionGlobalConfig } from "framer-motion";
import { isLowPowerDevice } from "./lib/low-power";
import App from "./App";
import "./index.css";
import "./lib/i18n";

// Tag the Android webview on <html> so CSS (via the `android:` variant) can
// skip expensive backdrop blurs that jank low-end devices. Must run before
// React renders so the very first paint is already blur-free.
if (Capacitor.getPlatform() === "android") {
  document.documentElement.classList.add("platform-android");
}

// Low-power mode for old/weak phones that run hot: skip ALL framer-motion
// animation work (87+ infinite ambient loops burn CPU/GPU nonstop) and tag
// <html> so CSS can drop blurred glow layers and pulse loops. Must run before
// React renders. Manual override: localStorage "unisync_low_power" = "1"/"0".
if (isLowPowerDevice()) {
  MotionGlobalConfig.skipAnimations = true;
  document.documentElement.classList.add("low-power");
}

/**
 * Capgo OTA — silent, automatic, zero user interaction.
 *
 * The Capgo native plugin (configured in capacitor.config.ts with
 * `autoUpdate: true` + `directUpdate: true`) handles the entire update
 * lifecycle on its own:
 *   - On every launch it checks for a newer bundle.
 *   - If one is downloaded and ready, it applies it during the native
 *     splash screen, BEFORE any JS runs. The user just opens the app
 *     and sees the new version. No reload, no toast, no "tap to update".
 *   - If a new bundle is found mid-session, it downloads in the background
 *     and is applied automatically the next time the user opens the app.
 *
 * Our ONLY responsibility from JS is to call `notifyAppReady()` as early
 * as possible so Capgo knows this bundle booted successfully and won't
 * roll it back. We fire it BEFORE React renders so even a slow first
 * paint can't push us past the rollback timeout.
 *
 * Critically, we do NOT call `set()` mid-session. Forcing an immediate
 * webview reload while the user is using the app was the source of the
 * "stuck on old bundle" failures — it would race the native auto-apply,
 * leave the bundle half-applied, and require users to delete/reinstall
 * to recover. Trusting the native plugin to apply on next launch is the
 * documented golden path and is rock-solid.
 */
async function notifyCapgoReady() {
  if (!Capacitor.isNativePlatform()) return;
  try {
    const { CapacitorUpdater } = await import("@capgo/capacitor-updater");
    await CapacitorUpdater.notifyAppReady();
  } catch (e) {
    console.error("[Capgo] notifyAppReady failed:", e);
  }
}

// Fire IMMEDIATELY at bundle load, before React mounts. Even if rendering
// hangs, Capgo will know this bundle launched successfully and won't roll
// it back. This is the single most important call for OTA reliability.
notifyCapgoReady();

// Hide the NATIVE splash as early as possible so the web-layer branded splash
// (#brand-splash in index.html) becomes what the user actually sees. The
// native image (the generic Capacitor logo on older builds) is baked into the
// binary and can't be changed over-the-air — but by handing off to our own
// branded screen the instant we boot, we shrink it to a brief launch flash.
// The branded #brand-splash stays up and covers the OTA gate / auth load; it
// is removed by App.tsx the moment the real app is ready.
async function hideNativeSplashEarly() {
  if (!Capacitor.isNativePlatform()) return;
  try {
    const { SplashScreen } = await import("@capacitor/splash-screen");
    await SplashScreen.hide({ fadeOutDuration: 200 });
  } catch {}
}
hideNativeSplashEarly();

// Hard safety net: never let the branded splash trap the user, even if React
// fails to mount. This fires regardless of App.tsx and is a little longer
// than the OTA gate's 6s ceiling.
setTimeout(() => {
  const el = document.getElementById("brand-splash");
  if (el) {
    el.style.animation = "none";
    el.classList.add("brand-splash--hide");
  }
}, 9000);

function getCrashReportUrl(): string {
  const isNative = typeof window !== "undefined" &&
    (window.location.protocol === "capacitor:" ||
     window.location.protocol === "ionic:" ||
     (window.location.hostname === "localhost" && window.location.port === ""));
  const base = isNative ? "https://uni-sync-ai--jka87.replit.app" : "";
  return `${base}/api/debug/client-error`;
}

window.addEventListener("error", (event) => {
  try {
    fetch(getCrashReportUrl(), {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        message: event.error?.message || event.message || "Unknown",
        name: event.error?.name || "WindowError",
        stack: event.error?.stack?.substring(0, 2000) || "",
        url: window.location.href,
        timestamp: new Date().toISOString(),
        source: "window.onerror",
      }),
    }).catch(() => {});
  } catch {}
});

window.addEventListener("unhandledrejection", (event) => {
  try {
    const reason = event.reason;
    fetch(getCrashReportUrl(), {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        message: reason?.message || String(reason) || "Unhandled rejection",
        name: reason?.name || "UnhandledRejection",
        stack: reason?.stack?.substring(0, 2000) || "",
        url: window.location.href,
        timestamp: new Date().toISOString(),
        source: "unhandledrejection",
      }),
    }).catch(() => {});
  } catch {}
});

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

if ("serviceWorker" in navigator && window.location.protocol !== "capacitor:") {
  // NEVER run the service worker on development/preview domains — a stale SW
  // there intercepts page loads and turns any dev-server hiccup into a
  // full-page "FetchEvent.respondWith received an error" screen. Actively
  // unregister anything previously installed on these hosts and drop its
  // caches so the preview always loads straight from the dev server.
  const isDevHost =
    /(\.replit\.dev|\.repl\.co|\.replit\.app\.localhost)$/.test(window.location.hostname) ||
    window.location.hostname === "localhost" ||
    window.location.hostname.startsWith("127.");

  if (isDevHost) {
    navigator.serviceWorker
      .getRegistrations()
      .then((regs) => regs.forEach((r) => r.unregister()))
      .catch(() => {});
    if ("caches" in window) {
      caches.keys().then((keys) => keys.forEach((k) => caches.delete(k))).catch(() => {});
    }
  } else {
    window.addEventListener("load", () => {
      navigator.serviceWorker.register("/sw.js").catch(() => {});
    });

    // When a NEW service worker takes control of this page (which happens after
    // a deploy bumps the SW), force a one-time reload so the page runs against
    // the fresh /assets/* bundles instead of trying to lazy-load old hashed
    // chunks that no longer exist on the server. Guarded so it only fires once
    // per session to avoid reload loops.
    let hasReloadedForNewSW = false;
    navigator.serviceWorker.addEventListener("controllerchange", () => {
      if (hasReloadedForNewSW) return;
      hasReloadedForNewSW = true;
      window.location.reload();
    });
  }
}
