import { lazy } from 'react';

declare const __APP_VERSION__: string | undefined;

const RELOAD_STORAGE_KEY = 'openchamber:chunk-import-reload';
const RETRY_DELAY_MS = 250;
const RELOAD_GUARD_MS = 30_000;

const DYNAMIC_IMPORT_ERROR_PATTERNS = [
  /Importing a module script failed/i,
  /Failed to fetch dynamically imported module/i,
  /error loading dynamically imported module/i,
  /Loading chunk \S+ failed/i,
  /ChunkLoadError/i,
];

function readErrorText(error: unknown): string {
  if (error instanceof Error) {
    return `${error.name}\n${error.message}\n${error.stack ?? ''}`;
  }

  if (typeof error === 'string') {
    return error;
  }

  try {
    return JSON.stringify(error);
  } catch {
    return String(error);
  }
}

function isDynamicImportError(error: unknown): boolean {
  const text = readErrorText(error);
  return DYNAMIC_IMPORT_ERROR_PATTERNS.some((pattern) => pattern.test(text));
}

function reloadMarkerSignature(error: unknown): string {
  const appVersion = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : 'unknown';
  return `${appVersion || 'unknown'}:${readErrorText(error).slice(0, 500)}`;
}

function scheduleReloadOnce(error: unknown): void {
  if (typeof window === 'undefined') return;

  const now = Date.now();
  const signature = reloadMarkerSignature(error);

  try {
    const rawMarker = window.sessionStorage.getItem(RELOAD_STORAGE_KEY);
    const marker = rawMarker ? JSON.parse(rawMarker) as { signature?: unknown; timestamp?: unknown } : null;
    const markerTimestamp = typeof marker?.timestamp === 'number' ? marker.timestamp : 0;
    if (marker?.signature === signature && now - markerTimestamp < RELOAD_GUARD_MS) {
      return;
    }
    window.sessionStorage.setItem(RELOAD_STORAGE_KEY, JSON.stringify({ signature, timestamp: now }));
  } catch {
    return;
  }

  window.setTimeout(() => {
    window.location.reload();
  }, 0);
}

export async function importWithChunkRecovery<T>(
  load: () => Promise<T>,
  options: { retries?: number } = {},
): Promise<T> {
  const retries = options.retries ?? 1;
  let lastError: unknown;

  for (let attempt = 0; attempt <= retries; attempt += 1) {
    try {
      return await load();
    } catch (error) {
      lastError = error;
      if (!isDynamicImportError(error) || attempt >= retries) {
        break;
      }

      await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS * (attempt + 1)));
    }
  }

  if (isDynamicImportError(lastError)) {
    scheduleReloadOnce(lastError);
  }

  throw lastError;
}

export const lazyWithChunkRecovery: typeof lazy = (load) => lazy(() => importWithChunkRecovery(load));
