// ─── Browser polyfills (must run before any imports) ─────────────────────────
// Object.hasOwn — Chrome 93+, Safari 15.4+ (2021). Old Android ad traffic crashes without this.
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-ignore
if (!Object.hasOwn) {
  // @ts-ignore
  Object.hasOwn = (obj: object, key: PropertyKey) =>
    Object.prototype.hasOwnProperty.call(obj, key);
}
// String.prototype.replaceAll — Chrome 85+. Prevent crashes on Android 6 / Chrome 80-era.
// @ts-ignore
if (!String.prototype.replaceAll) {
  // @ts-ignore
  String.prototype.replaceAll = function (search: string, replace: string): string {
    return this.split(search).join(replace);
  };
}
/* eslint-enable @typescript-eslint/ban-ts-comment */
// ─────────────────────────────────────────────────────────────────────────────

import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { reportError } from "./lib/error-reporter";
import { ErrorBoundary } from "./components/ErrorBoundary";

// Retire the old app-shell worker. It competed with Progressier's notification
// worker for control of the page and could reload the app during interaction.
// Keep this cleanup for returning visitors with the old worker installed.
if ("serviceWorker" in navigator) {
  void navigator.serviceWorker.getRegistrations().then((registrations) => {
    for (const registration of registrations) {
      const scriptUrl = registration.active?.scriptURL
        || registration.waiting?.scriptURL
        || registration.installing?.scriptURL
        || "";
      if (scriptUrl && new URL(scriptUrl).pathname === "/sw.js") {
        void registration.unregister();
      }
    }
  });
}

// ─── Global error tracking ────────────────────────────────────────────────────

/** Return true if the error is expected/irrelevant noise we don't want to log */
function isIgnorableError(message: string, source?: string | Event): boolean {
  // Cross-origin security suppression — no actionable info
  if (message === "Script error.") return true;
  // Supabase auth uses a Navigator LockManager to serialize token refreshes.
  // In multi-tab scenarios (or restricted browser environments) the lock can
  // time out waiting for another tab. This is a known SDK behaviour and NOT a
  // bug in our code — suppress to keep error logs clean.
  if (message.includes('Acquiring an exclusive Navigator LockManager lock') &&
      message.includes('auth-token')) return true;
  // External scripts (Progressier push, Google Tag Manager injections) can
  // call .click() on elements that no longer exist in the DOM.  These errors
  // originate from scripts we don't control and at line 1 of minified bundles.
  if (message.includes("Cannot read properties of null (reading 'click')")) return true;
  if (message.includes("null is not an object (evaluating") && message.includes("click")) return true;
  // Ignore errors from known external/third-party scripts
  const src = typeof source === "string" ? source : "";
  if (src.includes("progressier") || src.includes("googletagmanager") || src.includes("gtag")) return true;
  return false;
}

window.onerror = (message, source, lineno, colno, error) => {
  const msg = String(message);
  if (isIgnorableError(msg, source)) return false;
  reportError(
    "js_error",
    msg,
    error?.stack,
    { source, lineno, colno }
  );
  return false;
};

window.addEventListener("unhandledrejection", (event) => {
  const reason = event.reason;
  const message = reason instanceof Error ? reason.message : String(reason ?? "Unhandled rejection");
  if (isIgnorableError(message)) return;
  reportError(
    "promise_rejection",
    message,
    reason instanceof Error ? reason.stack : undefined
  );
});
// ─────────────────────────────────────────────────────────────────────────────

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

