import { trpc } from "@/lib/trpc";
import { UNAUTHED_ERR_MSG } from '@shared/const';
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink, TRPCClientError } from "@trpc/client";
import { createRoot } from "react-dom/client";
import React from "react";
import superjson from "superjson";
import App from "./App";
import { GamificationProvider } from "./contexts/GamificationContext";
import { ExportProvider } from "./contexts/ExportContext";
import { ClerkAuthFallback } from "./components/ClerkAuthBridge";
import { ProgressToastProvider } from "./components/ProgressToast";
import "./index.css";
import { preconnect, dnsPrefetch, perfMark, scheduleIdle } from "./lib/microOptimizations";

// P27: Mark app initialization start
perfMark('app-init-start');

// Global handler for chunk/module load failures from stale deployments.
// When a new version is deployed, old JS chunk filenames no longer exist.
// This catches the error globally and reloads the page once to pick up new chunks.
window.addEventListener('error', (event) => {
  const msg = event.message || '';
  if (
    msg.includes('Failed to fetch dynamically imported module') ||
    msg.includes('Loading chunk') ||
    msg.includes('is not a valid JavaScript MIME type') ||
    msg.includes('Importing a module script failed')
  ) {
    const reloadKey = 'chunk-reload-global';
    const lastReload = sessionStorage.getItem(reloadKey);
    const now = Date.now();
    if (!lastReload || (now - parseInt(lastReload, 10)) > 30000) {
      sessionStorage.setItem(reloadKey, String(now));
      console.warn('[ChunkReload] Stale chunk detected, reloading...');
      window.location.reload();
    }
  }
});

// Also catch unhandled promise rejections (dynamic import() returns a promise)
window.addEventListener('unhandledrejection', (event) => {
  const msg = event.reason?.message || String(event.reason) || '';
  if (
    msg.includes('Failed to fetch dynamically imported module') ||
    msg.includes('Loading chunk') ||
    msg.includes('is not a valid JavaScript MIME type') ||
    msg.includes('Importing a module script failed')
  ) {
    const reloadKey = 'chunk-reload-global';
    const lastReload = sessionStorage.getItem(reloadKey);
    const now = Date.now();
    if (!lastReload || (now - parseInt(lastReload, 10)) > 30000) {
      sessionStorage.setItem(reloadKey, String(now));
      console.warn('[ChunkReload] Stale chunk detected via promise rejection, reloading...');
      window.location.reload();
    }
  }
});

// PERFORMANCE OPTIMIZATION: Preconnect to critical origins
preconnect('https://financialmodelingprep.com');
preconnect('https://api.manus.im');
dnsPrefetch('https://fonts.googleapis.com');
dnsPrefetch('https://fonts.gstatic.com');
dnsPrefetch('https://api.polygon.io');

// Clerk publishable key from environment
const CLERK_PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;

// Satellite mode disabled — Clerk satellite requires dashboard configuration
// (CNAME records + domain registration in Clerk dashboard) which is not yet complete.
// Running in standard mode on all production domains. Users sign in via redirect
// to marlowekeynes.org/login and are redirected back after authentication.
const currentHost = typeof window !== "undefined" ? window.location.hostname : "";
const isSatelliteDomain = false; // Disabled until Clerk dashboard satellite config is complete

// Detect if we're on a dev proxy (Manus dev server, localhost)
const DEV_PROXY_PATTERNS = [".manus.computer", "localhost", "127.0.0.1"];
const isDevProxy = DEV_PROXY_PATTERNS.some(p => currentHost.includes(p));

// Clerk disabled — login is hidden for now. Re-enable when ready to activate auth.
// const shouldUseClerk = !isDevProxy && !!CLERK_PUBLISHABLE_KEY;
const shouldUseClerk = false;

// PERFORMANCE OPTIMIZATION: Configure QueryClient with better defaults
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      refetchOnReconnect: false,
      refetchOnMount: false, // R70: Don't refetch when component remounts (tab switch)
      retry: 1,
      staleTime: 1000 * 60 * 15, // R70: 15min global staleTime (was 5min) — data changes slowly
      gcTime: 1000 * 60 * 60, // R70: 60min garbage collection (was 30min)
    },
    mutations: {
      retry: 0,
    },
  },
});

// Log errors for debugging but don't auto-redirect
queryClient.getQueryCache().subscribe(event => {
  if (event.type === "updated" && event.action.type === "error") {
    const error = event.query.state.error;
    if (error instanceof TRPCClientError && error.message !== UNAUTHED_ERR_MSG) {
      console.error("[API Query Error]", error);
    }
  }
});

queryClient.getMutationCache().subscribe(event => {
  if (event.type === "updated" && event.action.type === "error") {
    const error = event.mutation.state.error;
    if (error instanceof TRPCClientError && error.message !== UNAUTHED_ERR_MSG) {
      console.error("[API Mutation Error]", error);
    }
  }
});

// PERFORMANCE OPTIMIZATION: Configure tRPC client with batching
const trpcClient = trpc.createClient({
  links: [
    httpBatchLink({
      url: "/api/trpc",
      transformer: superjson,
      maxURLLength: 2048,
      fetch(input, init) {
        return globalThis.fetch(input, {
          ...(init ?? {}),
          credentials: "include",
        });
      },
    }),
  ],
});

/**
 * Error boundary that catches Clerk initialization failures and falls back
 * to rendering the app without Clerk auth.
 */
class ClerkErrorBoundary extends React.Component<
  { children: React.ReactNode; fallback: React.ReactNode },
  { hasError: boolean }
> {
  constructor(props: { children: React.ReactNode; fallback: React.ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error("[ClerkErrorBoundary] Clerk failed, falling back:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}

// P27: Mark render start
perfMark('app-render-start');

const root = createRoot(document.getElementById("root")!);

/**
 * The core app tree WITHOUT any Clerk wrapper.
 * Used both as the standalone render and as the fallback for Clerk failures.
 */
function AppTree() {
  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <ClerkAuthFallback>
          <ExportProvider>
            <GamificationProvider>
              <ProgressToastProvider>
                <App />
              </ProgressToastProvider>
            </GamificationProvider>
          </ExportProvider>
        </ClerkAuthFallback>
      </QueryClientProvider>
    </trpc.Provider>
  );
}

if (shouldUseClerk) {
  /**
   * On production domains: dynamically import Clerk SDK and the auth bridge.
   * This ensures @clerk/clerk-react is NEVER loaded on dev server domains.
   *
   * SATELLITE MODE: When on a registered satellite domain, enable isSatellite
   * so Clerk syncs the session from the primary domain (marlowekeynes.org).
   * The satellite domain must be registered in Clerk dashboard AND have a
   * CNAME record: clerk.<domain> -> frontend-api.clerk.services
   *
   * STANDARD MODE: On unregistered domains, Clerk runs in standard mode.
   * Users can still sign in via redirect to marlowekeynes.org/login.
   */
  Promise.all([
    import("@clerk/clerk-react"),
    import("./components/ClerkAuthInner"),
  ]).then(([{ ClerkProvider }, { ClerkAuthInner }]) => {
    // Build ClerkProvider props based on whether this is a satellite domain
    const clerkProps: Record<string, unknown> = {
      publishableKey: CLERK_PUBLISHABLE_KEY,
      signInUrl: "https://marlowekeynes.org/login",
      afterSignInUrl: window.location.href,
      afterSignUpUrl: window.location.href,
      routerPush: (to: string) => window.location.assign(to),
      routerReplace: (to: string) => window.location.replace(to),
    };

    // Satellite mode disabled — would require:
    // 1. Register each domain in Clerk dashboard as satellite
    // 2. Add CNAME: clerk.<domain> -> frontend-api.clerk.services
    // 3. Re-enable isSatelliteDomain detection above
    // if (isSatelliteDomain) {
    //   clerkProps.isSatellite = true;
    //   clerkProps.domain = "marlowekeynes.org";
    // }

    root.render(
      <ClerkErrorBoundary fallback={<AppTree />}>
        <ClerkProvider {...(clerkProps as any)}>
          <trpc.Provider client={trpcClient} queryClient={queryClient}>
            <QueryClientProvider client={queryClient}>
              <ClerkAuthInner>
                <GamificationProvider>
                  <ProgressToastProvider>
                    <App />
                  </ProgressToastProvider>
                </GamificationProvider>
              </ClerkAuthInner>
            </QueryClientProvider>
          </trpc.Provider>
        </ClerkProvider>
      </ClerkErrorBoundary>
    );
  }).catch((err) => {
    console.error("[Clerk] Failed to load Clerk SDK, falling back:", err);
    root.render(<AppTree />);
  });
} else {
  root.render(<AppTree />);
}

// P16: Defer non-critical initialization to idle time
scheduleIdle(() => {
  perfMark('app-idle-init');
  if (document.fonts?.ready) {
    document.fonts.ready.then(() => perfMark('fonts-loaded'));
  }
}, 3000);
