// Safe WebSocket wrapper to prevent synchronous crashes on mobile/Safari
(function() {
  try {
    if (typeof window === 'undefined') return;

    const OriginalWebSocket = window.WebSocket;

    class SafeWebSocket {
      static readonly CONNECTING = 0;
      static readonly OPEN = 1;
      static readonly CLOSING = 2;
      static readonly CLOSED = 3;

      readonly CONNECTING = 0;
      readonly OPEN = 1;
      readonly CLOSING = 2;
      readonly CLOSED = 3;

      url: string;
      readyState: number;
      bufferedAmount: number;
      extensions: string;
      protocol: string;
      binaryType: BinaryType;

      onopen: ((this: WebSocket, ev: Event) => any) | null = null;
      onerror: ((this: WebSocket, ev: Event) => any) | null = null;
      onclose: ((this: WebSocket, ev: CloseEvent) => any) | null = null;
      onmessage: ((this: WebSocket, ev: MessageEvent) => any) | null = null;

      private _listeners: { [type: string]: Set<any> } = {};

      constructor(url: string | URL, protocols?: string | string[]) {
        this.url = String(url);
        this.readyState = SafeWebSocket.CLOSED;
        this.bufferedAmount = 0;
        this.extensions = "";
        this.protocol = "";
        this.binaryType = "blob";

        if (OriginalWebSocket) {
          try {
            // Attempt to instantiate the original WebSocket
            const ws = new OriginalWebSocket(url, protocols);
            // If successful, return the real WebSocket instance
            return ws as any;
          } catch (error) {
            console.warn(
              "WebSocket instantiation failed (likely blocked by browser security/private mode). Falling back to mock socket.",
              error
            );
          }
        } else {
          console.warn("WebSocket is not supported in this environment. Falling back to mock socket.");
        }

        // If original constructor failed or doesn't exist, we return a mock socket instance
        // Trigger errors asynchronously to match browser WebSocket behavior
        setTimeout(() => {
          const errorEv = {
            type: "error",
            target: this,
            message: "WebSocket connection blocked by security policy"
          };
          const closeEv = {
            type: "close",
            target: this,
            wasClean: false,
            code: 1006,
            reason: "Browser security block or lack of support"
          };

          // Call properties
          if (typeof this.onerror === "function") {
            try { this.onerror(errorEv as any); } catch (e) { console.error(e); }
          }
          if (typeof this.onclose === "function") {
            try { this.onclose(closeEv as any); } catch (e) { console.error(e); }
          }

          // Call addEventListener listeners
          if (this._listeners["error"]) {
            this._listeners["error"].forEach(listener => {
              try { listener(errorEv); } catch (e) { console.error(e); }
            });
          }
          if (this._listeners["close"]) {
            this._listeners["close"].forEach(listener => {
              try { listener(closeEv); } catch (e) { console.error(e); }
            });
          }
        }, 50);
      }

      close() {
        this.readyState = SafeWebSocket.CLOSED;
      }

      send() {
        if (this.readyState === SafeWebSocket.CONNECTING) {
          throw new DOMException("An attempt was made to use an object that is not, or is no longer, usable", "InvalidStateError");
        }
      }

      addEventListener(type: string, listener: any) {
        if (!this._listeners[type]) {
          this._listeners[type] = new Set();
        }
        this._listeners[type].add(listener);
      }

      removeEventListener(type: string, listener: any) {
        if (this._listeners[type]) {
          this._listeners[type].delete(listener);
        }
      }

      dispatchEvent() {
        return true;
      }
    }

    // Assign properties on the constructor to match native WebSocket
    Object.defineProperty(SafeWebSocket, 'CONNECTING', { value: 0, writable: false });
    Object.defineProperty(SafeWebSocket, 'OPEN', { value: 1, writable: false });
    Object.defineProperty(SafeWebSocket, 'CLOSING', { value: 2, writable: false });
    Object.defineProperty(SafeWebSocket, 'CLOSED', { value: 3, writable: false });

    (window as any).WebSocket = SafeWebSocket;
    console.log("Registered global SafeWebSocket wrapper successfully.");
  } catch (e) {
    console.error("Error setting up SafeWebSocket wrapper:", e);
  }
})();

import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import './styles/mobile-ux.css'
import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3'
import { initSentry, Sentry } from './lib/sentry'

// Initialize Sentry as early as possible
initSentry();

const STALE_ASSET_RECOVERY_KEY = 'cosmoquick_stale_asset_recovery_v3';
const SW_RETIREMENT_KEY = 'cosmoquick_sw_retired_v1';

const getSafeSessionValue = (key: string): string | null => {
  try {
    return sessionStorage.getItem(key);
  } catch {
    return null;
  }
};

const setSafeSessionValue = (key: string, value: string) => {
  try {
    sessionStorage.setItem(key, value);
  } catch {
    // ignore storage access issues
  }
};

type ErrorLike = {
  message?: unknown;
  reason?: {
    message?: unknown;
  };
};

const getErrorMessage = (error: unknown): string => {
  if (typeof error === 'string') {
    return error;
  }

  if (typeof error === 'object' && error !== null) {
    const maybeError = error as ErrorLike;

    if (typeof maybeError.message === 'string') {
      return maybeError.message;
    }

    if (typeof maybeError.reason?.message === 'string') {
      return maybeError.reason.message;
    }
  }

  return String(error);
};

const clearCachesAndUnregisterSW = async () => {
  try {
    if ('caches' in window) {
      const cacheNames = await caches.keys();
      await Promise.all(cacheNames.map((name) => caches.delete(name)));
    }
    if ('serviceWorker' in navigator) {
      const regs = await navigator.serviceWorker.getRegistrations();
      await Promise.all(regs.map((r) => r.unregister()));
    }
  } catch {
    // ignore
  }
};

const reportMainThreadError = (error: unknown, context: string) => {
  try {
    const exception = error instanceof Error ? error : new Error(String(error));

    Sentry.withScope((scope) => {
      scope.setTag('error_context', context);
      Sentry.captureException(exception);
    });
  } catch {
    // ignore reporting failures
  }
};

const registerServiceWorker = async () => {
  if (!('serviceWorker' in navigator)) return;

  // Skip SW in Lovable preview/iframe — prevents stale shell in editor
  const isInIframe = (() => {
    try { return window.self !== window.top; } catch { return true; }
  })();
  const host = window.location.hostname;
  const isPreviewHost =
    host.includes('lovableproject.com') ||
    host.includes('lovable.app') ||
    host.includes('id-preview--') ||
    host === 'localhost' ||
    host === '127.0.0.1';

  if (isInIframe || isPreviewHost) {
    // Make sure no stale SW lingers in preview
    try {
      const regs = await navigator.serviceWorker.getRegistrations();
      await Promise.all(regs.map((r) => r.unregister()));
    } catch {/* ignore */}
    return;
  }

  try {
    // Reset retirement flag from older builds and register the real SW
    localStorage.removeItem(SW_RETIREMENT_KEY);
    await navigator.serviceWorker.register('/sw.js', { scope: '/' });
  } catch (e) {
    console.log('Service worker registration failed:', e);
  }
};


// Delay SW registration so it doesn't compete with initial chunk loads
if (document.readyState === 'complete') {
  registerServiceWorker();
} else {
  window.addEventListener('load', () => {
    // Wait a bit after load to avoid competing with lazy imports
    setTimeout(registerServiceWorker, 2000);
  });
}

// Error boundary fallback for critical errors
const showErrorFallback = (error: Error) => {
  const root = document.getElementById('root');
  if (root) {
    root.innerHTML = `
      <div style="min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #f8fafc; font-family: system-ui, -apple-system, sans-serif;">
        <div style="text-align: center; padding: 40px 32px; background: white; border-radius: 16px; box-shadow: 0 4px 24px rgba(0,0,0,0.08); max-width: 440px; margin: 20px;">
          <div style="width: 56px; height: 56px; background: #fef2f2; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; font-size: 24px;">⚠️</div>
          <h1 style="color: #1e293b; margin: 0 0 8px; font-size: 20px; font-weight: 600;">Something went wrong</h1>
          <p style="color: #64748b; margin: 0 0 24px; font-size: 14px; line-height: 1.5;">We're experiencing a temporary issue. Please try refreshing the page.</p>
          <div style="display: flex; gap: 12px; justify-content: center;">
            <button onclick="window.location.reload()" style="background: #2563eb; color: white; border: none; padding: 10px 24px; border-radius: 8px; font-size: 14px; cursor: pointer; font-weight: 500;">
              Refresh Page
            </button>
            <button onclick="clearCacheAndReload()" style="background: transparent; color: #2563eb; border: 1px solid #2563eb; padding: 10px 24px; border-radius: 8px; font-size: 14px; cursor: pointer; font-weight: 500;">
              Clear Cache
            </button>
          </div>
          <p style="color: #94a3b8; font-size: 11px; margin-top: 20px; word-break: break-word;">Error: ${error.message || 'Unknown error'}</p>
        </div>
      </div>
    `;

    const windowWithRecovery = window as Window & {
      clearCacheAndReload?: () => Promise<void>;
    };

    windowWithRecovery.clearCacheAndReload = async () => {
      try {
        await clearCachesAndUnregisterSW();
        localStorage.clear();
        sessionStorage.clear();
        window.location.reload();
      } catch {
        window.location.reload();
      }
    };
  }
};

const isStaleChunkError = (err: unknown) => {
  const msg = getErrorMessage(err);
  return /Loading chunk|ChunkLoadError|Failed to fetch dynamically imported module|error loading dynamically imported module|Importing a module script failed|Unable to preload CSS|Unexpected token '<'|expected expression, got '<'/i.test(
    msg
  );
};

const recoverFromStaleAssets = async () => {
  const attempts = parseInt(getSafeSessionValue(STALE_ASSET_RECOVERY_KEY) || '0', 10);
  if (attempts >= 3) {
    console.error('Stale asset recovery exhausted. Preventing infinite reload loop.');
    return;
  }
  setSafeSessionValue(STALE_ASSET_RECOVERY_KEY, String(attempts + 1));
  await clearCachesAndUnregisterSW();
  // Brief delay to allow service worker unregistration database operations to complete
  setTimeout(() => {
    window.location.reload();
  }, 400);
};

// Wrap the app rendering in try-catch
try {
  const rootElement = document.getElementById('root');

  if (!rootElement) {
    throw new Error('Root element not found');
  }

  const root = createRoot(rootElement);

  root.render(
    <GoogleReCaptchaProvider
      reCaptchaKey="6LdtXsQrAAAAAFdF3meHzSzrQXuxK0XKLqtA8zYL"
      scriptProps={{ async: true, defer: true }}
    >
      <App />
    </GoogleReCaptchaProvider>
  );
} catch (error) {
  console.error('Failed to render app:', error);
  reportMainThreadError(error, 'initial_render');
  showErrorFallback(error as Error);
}

// Global error handler for unhandled errors
window.onerror = (message, source, lineno, colno, error) => {
  console.error('Global error:', { message, source, lineno, colno, error });

  if (isStaleChunkError(error || message)) {
    void recoverFromStaleAssets();
    return;
  }

  // Only show fallback if the root is completely empty (app didn't mount)
  if (document.getElementById('root')?.innerHTML === '') {
    showErrorFallback(error || new Error(String(message)));
  }
};

// Handle unhandled promise rejections
window.onunhandledrejection = (event) => {
  console.error('Unhandled promise rejection:', event.reason);

  if (isStaleChunkError(event.reason)) {
    void recoverFromStaleAssets();
  }
};
