// MUST stay the first import: it swaps in a memory store when the browser blocks
// site data, before any other module touches localStorage. See safeStorage.ts.
import { storageFallback } from './utils/storage/safeStorage';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import { ThemeProvider } from "next-themes";
import * as Sentry from "@sentry/react";
import App from './App.lazy.tsx';
import './index.css';
import { applySecurityHeaders } from './utils/security/securityHeaders';
import { initializeProductionLogging, logger } from './utils/logging/standardLogger';

// Initialize Sentry error monitoring
const shouldSuppressKnownSentryFeatureError = (reason: unknown): boolean => {
  const message =
    typeof reason === "string"
      ? reason
      : reason instanceof Error
        ? reason.message
        : "";

  return message.includes("performanceMetrics") && message.includes("feature named");
};

// Some environments (notably certain iOS in-app browsers) can surface a non-fatal
// Sentry SDK internal rejection about missing optional performance metrics.
// Suppress only that specific rejection to avoid user impact + noisy alerts.
window.addEventListener("unhandledrejection", (event) => {
  if (shouldSuppressKnownSentryFeatureError(event.reason)) {
    event.preventDefault();
  }
});

Sentry.init({
  dsn: "https://f8aab895d01ab2fc676e2f9f90030aa8@o4510183432126464.ingest.us.sentry.io/4510183436124160",
  integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
  tracesSampleRate: 1.0,
  // 0 on purpose: random sampling mostly recorded the 6-hourly smoke test and
  // error-free login visits, and burned the 50/month replay quota. Errors still record.
  replaysSessionSampleRate: 0,
  replaysOnErrorSampleRate: 1.0,
  environment: import.meta.env.MODE,
  sendDefaultPii: true,
  beforeSend(event) {
    // Filter out expected/transient errors
    const errorMessage = event.exception?.values?.[0]?.value || "";
    const errorType = event.exception?.values?.[0]?.type || "";

    // Suppress known non-fatal Sentry SDK internal feature errors
    if (errorMessage.includes("feature named") && errorMessage.includes("was not found")) {
      return null;
    }

    // Dynamic import/chunk load errors - handled by lazyWithRetry
    if (
      errorMessage.includes("Failed to fetch dynamically imported module") ||
      errorMessage.includes("Importing a module script failed") ||
      errorMessage.includes("Unable to preload CSS") ||
      errorMessage.includes("ChunkLoadError")
    ) {
      return null;
    }

    // Stripe CSP eval errors - Stripe works despite these
    if (errorType === "EvalError" && errorMessage.includes("Content Security Policy")) {
      return null;
    }

    // JWT token errors - handled by SimpleAuthContext (clears session + redirects)
    if (errorMessage.includes("InvalidJWTToken") || errorMessage.includes("Invalid value for JWT claim")) {
      return null;
    }

    // Supabase Auth Web Locks API timeout - known browser race condition, non-fatal
    if (errorMessage.includes("Navigator LockManager") && errorMessage.includes("timed out")) {
      return null;
    }

    // React hot-reload context errors - transient during HMR
    if (
      errorMessage.includes("useAuth must be used within an AuthProvider") ||
      errorMessage.includes("must be used within a") ||
      errorMessage.includes("Cannot read properties of null")
    ) {
      return null;
    }

    // AbortError from media fetches - user navigated away while media was loading
    if (errorType === "AbortError" || errorMessage.includes("aborted by the user")) {
      return null;
    }

    return event;
  },
});

// Initialize security measures
applySecurityHeaders();
initializeProductionLogging();

// Site data is blocked (in-app browser / private tab). The app runs on an in-memory
// store instead of dying; worth knowing about because the session cannot persist.
if (storageFallback.local || storageFallback.session) {
  logger.warn(
    "Browser storage is blocked; using an in-memory fallback",
    { local: storageFallback.local, session: storageFallback.session },
    { component: "main" }
  );
}

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 1000, // 1 minute
      refetchOnWindowFocus: false,
      retry: 2,
    },
  },
});

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <ThemeProvider attribute="class" defaultTheme="light" enableSystem>
        <TooltipProvider>
          <App />
          <Toaster />
        </TooltipProvider>
      </ThemeProvider>
    </QueryClientProvider>
  </StrictMode>,
);
