import { crypto } from '@chat-app/shared';
import { type ReactNode, useEffect, useState } from 'react';

import { createLibsodiumBackend } from '../lib/cryptoBackend';
import { BootError } from './BootError';
import { BootSplash } from './BootSplash';

interface Props {
  children: ReactNode;
}

// React Native exposes ErrorUtils on the global. The types ship with RN but
// we cast defensively because the renderer used by Vitest does not.
interface RNErrorUtils {
  getGlobalHandler: () => (err: Error, isFatal?: boolean) => void;
  setGlobalHandler: (handler: (err: Error, isFatal?: boolean) => void) => void;
}

// Initialises the crypto backend inside a useEffect (not at module-eval) so
// any failure surfaces in the React tree. Also installs a global JS error
// handler that routes unhandled throws to BootError; this catches errors
// thrown during render (e.g. the lazy env proxy reading a missing var) that
// would otherwise escape every per-screen ErrorBoundary.
export function AppBootstrap({ children }: Props) {
  const [error, setError] = useState<Error | null>(null);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    try {
      crypto.setCryptoBackend(createLibsodiumBackend());
      setReady(true);
    } catch (e: unknown) {
      setError(e instanceof Error ? e : new Error(String(e)));
    }
  }, []);

  useEffect(() => {
    const eu = (globalThis as unknown as { ErrorUtils?: RNErrorUtils }).ErrorUtils;
    if (!eu) return;
    const prev = eu.getGlobalHandler();
    eu.setGlobalHandler((err, isFatal) => {
      prev?.(err, isFatal);
      setError(err);
    });
    return () => eu.setGlobalHandler(prev);
  }, []);

  if (error) return <BootError error={error} />;
  if (!ready) return <BootSplash />;
  return <>{children}</>;
}
